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

时间:2017-05-22 02:26:24

标签: hibernate jpa persistence h2 ejb-3.0

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

员工类

    @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 onethis post,但我仍然无法找到解决此问题的方法。另外两个持久化函数被定义为

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

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

错误是在em.persistent(员工)处引发的。

4 个答案:

答案 0 :(得分:2)

查看您的Employee实体,您已将Department配置为Cascade ALl 这意味着只要员工实体PERSIST, MERGE, REMOVE, REFRESH, DETACH对部门执行相同操作。

现在回到你的问题。在populateTable功能中 您已创建DepartmentEmployee实体 您需要像这样链接DepartmentEmployee

e1.setDepartment(d1);

并最终坚持员工,这将为员工和部门保留

答案 1 :(得分:1)

我认为问题是你的服务类。由于保存@Transactional d1时服务类为service.addDepartment(d1);,因此当函数返回时,d1将被分离并且不再处于持久状态,然后在下一行当您尝试保存包含e1的{​​{1}} service.addEmployee(e1);时,您显然会收到d1.getEmployees().add(e1)此错误。因为您的detached entity passed to persist:对象已处于分离状态,并且您尝试将其保留在另一个d1方法中。我认为您可以将两种服务方法合并为一种以节省部门和员工,并且可以忽略此错误,或者您可以在下一个服务方法中使用@Transactional操作,如下所示:

merge

答案 2 :(得分:1)

在我的春季启动项目中,我遇到了类似的问题,但是这里的解决方案很轻松。我意识到,只要在父对象之前持久保留子对象,即总是不要将Cascade.ALL约束放在子实体上,总是会得到分离的持久性异常。

答案 3 :(得分:0)

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