且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2022-11-04 22:25:26

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

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 Project instance to be detached from the EntityManager, so that it cannot be included in a persist operation.

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

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

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

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

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

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(…).