15

想象以下模型:

员工:

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

1 回答 1

20

我猜您正在与存储库进行交互而没有适当地扩展事务边界。默认情况下,事务(以及会话)边界位于存储库方法级别。这会导致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(…). 所以第一种方法会导致 apersist(…)被触发,第二种方法会导致 a merge(…)

于 2013-04-11T08:23:48.997 回答