JPA/Hibernate 分离的实体传递给持久化

JPA/Hibernate detached entity passed to persist

JPA 和休眠的新手,所以请耐心等待我尝试绘制完整的图片。所以我有两个具有双向关系的 Java 对象。 Employee class 是所有者 class,Department class 是关系的反面。一个部门可以有很多员工,一个员工只能有一个部门。我指定 employee_id 作为员工实体的主键,department_id 作为部门实体的主键。我还想使用 department_id 作为员工 class 中的外键。

员工class

    @Entity
    @Table(name = "Employee")
    public class Employee
    implements java.io.Serializable
    {
       private static final long serialVersionUID = 1L;
       //primary key
       @Id
       @GeneratedValue(strategy = GenerationType.IDENTITY)
       @Column(name = "Employee_ID")
       private Integer       id;

       //foreign key 
       @ManyToOne(cascade=CascadeType.ALL)
       @JoinColumn(name = "Department_ID")
       private Department department;

       @Column(name = "Employee_name")
       private String    name;

       public Employee() { }

       public Employee(Integer id)
       {
          this.setId(id);
       }

       public void setDepartment(Department department){
           this.department = department;
       }
       public Department getDepartment(){
           return department;
       }
       public Integer getId() { return id; }
       public void setId(Integer id) { this.id = id; }
       public String getEmployeeName(){
            return name;
        }

        public void setEmployeeName(String name){
            this.name = name;
        }

部门class

@Entity
@Table(name = "Department")
public class Department
implements java.io.Serializable
{
   private static final long serialVersionUID = 1L;
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "Department_ID")
   private Integer       id;

   @Column(name = "Department_name")
   private String    name;

   @OneToMany(mappedBy="department", cascade=CascadeType.ALL)
   private Collection<Employee> employees = new ArrayList<Employee>();

   public Collection<Employee> getEmployees() {return employees;}
   public void setEmployees(Collection<Employee> e){ employees = e;}

   public Department() { }

   public Department(Integer id)
   {
      this.setId(id);
   }

   public Integer getId() { return id; }
   public void setId(Integer id) { this.id = id; }
   public String getDepartmentName(){
        return name;
    }

    public void setDepartmentName(String name){
        this.name = name;
    }

我用来生成两个对象并将它们保存在数据库中的函数。我正在测试的数据库是 H2,persistence.xml 文件配置为最初删除所有表,因此在调用以下函数之前所有表都是空的。

public static void populateTable() {
        System.out.println("Populating tables");
        Department d1 = new Department();
        //d1.setId(1);
        d1.setDepartmentName("finance");
        //service.addDepartment(d1);

        Employee e1 = new Employee();
        e1.setEmployeeName("John");
        //e1.setId(1);
        //e1.setDepartment(d1);
        //d1.getEmployees().add(e1);
        service.addDepartment(d1);
        service.addEmployee(e1);
}

最初我尝试自己设置创建对象的主要 ID,但出现了 "detached entity passed to persist" 错误。看了一个类似的question, I understand that if I try to set the unique identifier before persisting the object, JPA will think the entity is already present in the database and it will throw a "detached entity passed to persist error". So I commented out callers to setter functions and above code runs fine. However if I try to link an employee to a department like what I did with e1.setDepartment(d1); I would get this error detached entity passed to persist: com.javatunes.jpa.catalog.Department. I have read many blogs such as this one and this post,但是还是找不到解决这个问题的办法。还有一件事,这两个持久函数被定义为

public void addEmployee(Employee employee) {
          em.persist(employee);
          em.flush();
          }

public void addDepartment(Department department){
          em.persist(department);
          em.flush();
          }

错误发生在 em.persistent(员工)。

查看您的 Employee 实体,您已将 Department 配置为 Cascade ALL 这意味着只要 Employee 实体是 PERSIST, MERGE, REMOVE, REFRESH, DETACH 就会对 Department 做同样的事情。

现在回到你的问题。在你的 populateTable 函数中 您已经创建了 DepartmentEmployee 实体 你需要像这样 link DepartmentEmployee

e1.setDepartment(d1);

最后持久化员工,它将为您持久化员工和部门

我认为问题出在您的服务上 class。因为当你保存 d1 service.addDepartment(d1); 时你的服务 class 是 @Transactional 然后当函数 returns 然后 d1 被分离并且它不再在持久状态,然后在下一行尝试保存 e1 service.addEmployee(e1); 包括 d1.getEmployees().add(e1) 时,显然会得到 detached entity passed to persist: 这个错误。因为您的 d1 对象已经处于分离状态,并且您正试图将其保存在另一个 @Transactional 方法中。我认为您可以将两种服务方法合并为一个以保存部门和员工,并且可以简单地忽略此错误,或者您可以在下一个服务方法中使用 merge 操作,如下所示:

public void addDepartment(Department department) {
    em.merge(department);
    em.flush();
}

感谢@Ashish451,错误在于在我的服务代码中,我保留了 department (d1)。但是,如果我尝试先 link 员工 (e1) 和部门 (d1) 然后保留员工,JPA 将不会高兴,因为部门 (d1) 已经保留并且 persist() 仅有效在瞬态对象或新创建的实例上。

我在 spring 引导项目中遇到了类似的问题,但这里的解决方案很容易工作。我意识到只要你在父对象之前持久化一个子对象,你总是会得到分离的持久性异常,即你永远不应该在子实体上放置 Cascade.ALL 约束。