2013-04-11 141 views
12

想象一下以下机型:如何在Spring JPA中保存引用现有实体的新实体?

员工:

@ManyToMany(cascade = CascadeType.ALL) 
@JoinTable(name = "employee_project", joinColumns = @JoinColumn(name = "Emp_Id"), inverseJoinColumns = @JoinColumn(name = "Proj_id")) 
private Set<Project> projects = new HashSet<Project>(); 

项目:

@ManyToMany(mappedBy = "projects") 
private Set<Employee> employees = new HashSet<Employee>(); 

现在,如果我创建一个新的员工是指现有的项目,并尝试坚持该员工,我得到一个错误:

detached entity passed to persist: Project 

我创造了就业饴如下:

public void createNewEmployee(EmployeeDTO empDTO) { 

    Employee emp = new Employee(); 
    // add stuff from DTO, including projects 

    repository.saveAndFlush(emp); // FAILS 
} 

,我更新现有的像这样:

public void updateEmployee(EmployeeDTO empDTO) { 

    Employee emp = repository.findOne(empDTO.getId()); 
    // set stuff from DTO, including projects 

    repository.saveAndFlush(emp); // WORKS! 
} 

回答

19

我猜你不恰当地扩大事务边界与存储库交互。默认情况下,事务(因此会话)边界位于存储库方法级别。这会导致Project实例与EntityManager分离,因此它不能包含在持久操作中。

这里的解决方案是扩展事务边界到客户端:

@Component 
class YourRepositoryClient { 

    private final ProjectRepository projects; 
    private final EmployeeRepository employees; 

    // … constructor for autowiring 

    @Transactional 
    public void doSomething() { 
    Project project = projects.findOne(1L); 
    Employee employee = employees.save(new Employee(project)); 
    } 
} 

这种方法导致Project实例留一个管理实体,从而用于新鲜Employee实例被正确地处理将被执行的操作的持续。

与两个存储库交互的不同之处在于,第二种情况下,您将拥有一个已分离的实例(已被持久存在,具有一个id集合),在第一个示例中,您有完全不受管理的实例没有一个id集。 id属性是导致存储库区分调用persist(…)merge(…)的原因。所以第一种方法会导致persist(…)被触发,第二种会导致merge(…)

+0

谢谢,这是有效的,但我仍然想知道为什么我的更新方法没有从他们的存储库中获取项目(请参阅我更新的问题的详细信息) – wannabeartist 2013-04-11 08:48:56

+1

在此处扩展问题并不是一个好主意,因为它们使某种程度上的答案。最好提出新的问题。我会相应地更新我的答案。 – 2013-04-11 10:13:29

+0

那是春天的'@ Transactional'还是JPA?还是它重要? – CorayThan 2014-10-31 07:01:02

相关问题