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

2022-09-03 13:55:41

想象一下以下模型:

员工:

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

答案 1

我猜你正在与存储库交互,而没有适当地扩展事务边界。默认情况下,事务(以及会话)边界位于存储库方法级别。这会导致实例与 分离,因此无法将其包含在持久化操作中。ProjectEntityManager

此处的解决方案是将事务边界扩展到客户端:

@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));
  }
}

此方法使实例保持为托管实体,从而为正确处理的新鲜实例执行持久操作。ProjectEmployee

与两个存储库交互的区别在于,在第二种情况下,您将拥有一个分离的实例(已持久化,具有 id 集),其中与第一个示例中一样,您有一个完全非托管的实例,该实例没有 id 集。id 属性是导致存储库区分调用 和 .因此,第一种方法将导致触发 a,第二种方法将导致 .persist(…)merge(…)persist(…)merge(…)


答案 2

推荐