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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15943783/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 21:22:19  来源:igfitidea点击:

How to save a new entity that refers existing entity in Spring JPA?

javaspringjpaspring-dataspring-data-jpa

提问by wannabeartist

Imagine the following models:

想象一下以下模型:

Employee:

员工:

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

Project:

项目:

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

Now if I create a new employee that refers to an existing project and try to persist that employee, I get an error:

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

detached entity passed to persist: Project

I create the employee as follows:

我创建员工如下:

public void createNewEmployee(EmployeeDTO empDTO) {

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

  repository.saveAndFlush(emp);  // FAILS
}

and I update existing ones like this:

我像这样更新现有的:

public void updateEmployee(EmployeeDTO empDTO) {

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

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

回答by Oliver Drotbohm

I guess you're interacting with the repository without expanding the transaction boundaries appropriately. By default, the transaction (and thus session) boundary is at the repository method level. This causes the Projectinstance to be detached from the EntityManager, so that it cannot be included in a persist operation.

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

The solution here is to extend the transaction boundary to the client:

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

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

This approach causes the Projectinstance stay a managed entity and thus the persist operation to be executed for the fresh Employeeinstance being handled correctly.

这种方法使Project实例保持为受管实体,从而为Employee正确处理的新实例执行持久操作。

The difference with the two repository interactions is that in the second case you'll have a detached instance (has already been persisted, has an id set), where as in the first example you have a completely unmanaged instances that does not have an id set. The id property is what causes the repository to differentiate between calling persist(…)and merge(…). So the first approach will cause a persist(…)to be triggered, the second will cause a merge(…).

两个存储库交互的不同之处在于,在第二种情况下,您将拥有一个分离的实例(已被持久化,设置了 id),而在第一个示例中,您有一个完全非托管的实例,它没有 id放。id 属性是导致存储库区分调用persist(…)merge(…). 所以第一种方法会导致 apersist(…)被触发,第二种方法会导致 a merge(…)