如何在Spring JPA中保存引用现有实体的新实体?

想象一下以下型号:

雇员:

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

项目:

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

现在,如果我创建一个引用现有项目的新员工并尝试坚持该员工,我会收到一个错误:

 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! } 

我猜你正在与存储库进行交互而不适当地扩展事务边界。 默认情况下,事务(以及会话)边界位于存储库方法级别。 这会导致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(…)