【问题标题】:How to save a new entity that refers existing entity in Spring JPA?如何保存引用 Spring JPA 中现有实体的新实体?
【发布时间】:2013-04-03 08:28:21
【问题描述】:

想象以下模型:

员工:

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

【问题讨论】:

    标签: java spring jpa spring-data spring-data-jpa


    【解决方案1】:

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

    【讨论】:

    • 谢谢,这行得通,但我仍然想知道为什么我的更新方法可以在不从其存储库中获取项目的情况下工作(有关详细信息,请参阅我更新的问题)
    • 在这里扩展问题不是一个好主意,因为它们在某种程度上使答案无效。最好提出新问题。我会相应地更新我的答案。
    • 是春天的@Transactional 还是JPA 的,还是有关系?
    • 春天的。 JPA 中没有@Transactional。一般来说,使用您喜欢的任何事务控制机制(例如 Spring 注释、EJB 事务注释、JTA 1.2 的@Transactional)。
    • 没有预检怎么办findOne?我的意思是强制 Spring Data 检测数据库中已经存在的子对象(通过给定id)并且应该只是merge-it(自动,由 Spring Data,而不是由我!!!)??? Spring Data 有这么方便吗?
    猜你喜欢
    • 2013-05-09
    • 1970-01-01
    • 2015-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-14
    • 1970-01-01
    相关资源
    最近更新 更多