【问题标题】:JPA/Hibernate detached entity passed to persistJPA/Hibernate 分离实体传递给持久化
【发布时间】:2017-10-21 13:47:10
【问题描述】:

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

员工类

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

部门类

@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,但出现“分离实体传递给持久”错误。在阅读了类似的question 之后,我了解到如果我尝试在持久化对象之前设置唯一标识符,JPA 会认为该实体已经存在于数据库中,并且会抛出“传递给持久化错误的分离实体”。所以我注释掉了 setter 函数的调用者,上面的代码运行良好。但是,如果我尝试将员工链接到部门,就像我对e1.setDepartment(d1); 所做的那样,我会收到此错误detached entity passed to persist: com.javatunes.jpa.catalog.Department。我已经阅读了很多博客,例如this one 和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) 处引发错误。

【问题讨论】:

    标签: hibernate jpa persistence h2 ejb-3.0


    【解决方案1】:

    我在我的 Spring Boot 项目中遇到了类似的问题,但这里的解决方案很容易奏效。我意识到,每当您在父对象之前持久化子对象时,您总会得到分离持久性异常,即您永远不应该将 Cascade.ALL 约束放在子实体上。

    【讨论】:

      【解决方案2】:

      我认为问题在于您的服务等级。由于您的服务类是@Transactional,当您保存d1 service.addDepartment(d1); 时,当函数返回时,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();
      }
      

      【讨论】:

        【解决方案3】:

        感谢@Ashish451,错误在于在我的服务代码中,我坚持了部门(d1)。但是,如果我尝试先链接员工(e1)和部门(d1)然后持久化员工,JPA 将不高兴,因为部门(d1)已经持久化并且persist() 仅适用于瞬态对象或新创建的对象实例。

        【讨论】:

          【解决方案4】:

          查看您的 Employee 实体,您已将 Department 配置为 Cascade ALL 这意味着只要 Employee 实体是 PERSIST, MERGE, REMOVE, REFRESH, DETACH ,就会对 Department 执行相同的操作。

          现在回到你的问题。在您的 populateTable 函数中 您已创建 Department 和 Employee 实体 您需要像这样链接Department 和Employee

          e1.setDepartment(d1);
          

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-06-26
            • 2018-01-05
            • 2015-12-26
            • 1970-01-01
            • 2012-11-02
            相关资源
            最近更新 更多