0

new to JPA and hibernate, so please bear with me as I try to draw up a whole pic. So I have two Java objects that have bidirectional relationship. Employee class is the owning class and Department class is the inverse side of the relationship. A department can have many employees and a employee can have only one department. I assigned employee_id to be the primary key of the employee entity and department_id as the primary key of the department entity. I also want to use department_id as the foreign key in the employee class.

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

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

Function I use to generate two objects and persist them in the database. The database I am testing with is H2 and the persistence.xml file is configured to drop all the tables initially so all tables is empty before the below function is called.

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

Initially I tried to set the primary ids of created objects myself and I was getting "detached entity passed to persist" error. After reading a similar 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, but I still cannot find a solution to this problem. And one more thing the two persisting functions are defined as

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

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

The error is raised at em.persistent(employee).

Community
  • 1
  • 1
Pupusa
  • 167
  • 1
  • 3
  • 14

4 Answers4

2

Looking at your Employee entity, you have configured Department to Cascade ALl which means whenever Employee entity is PERSIST, MERGE, REMOVE, REFRESH, DETACH will do the same for Department.

Now coming back to your problem. In your populateTable function you have created Department and Employee entity you need to link Department and Employee like this

e1.setDepartment(d1);

and finally persist employee which will persist both Employee and Department for you

Pupusa
  • 167
  • 1
  • 3
  • 14
MyTwoCents
  • 7,284
  • 3
  • 24
  • 52
2

I had a similar problem in my spring boot project but the solution here worked with ease. I realized you will always get the detached persistence exception whenever you persist a child object before the parent i.e You should never place the Cascade.ALL constraint on the child entity.

Paul Ntalo
  • 21
  • 3
1

I think the problem is your service class. Since your service class is @Transactional when you save d1 service.addDepartment(d1); then when the function returns then d1 gets detached and it is no longer in persistent state and then in the next line when you try to save e1 service.addEmployee(e1); including d1.getEmployees().add(e1), then you will obviously get detached entity passed to persist: this error. Because your d1 object is already in detached state and you are trying to persist this in another @Transactional method. I think either you can merge the two service methods into one to save department and employee and this error can be simply ignored or you can use merge operation in next service method like this:

public void addDepartment(Department department) {
    em.merge(department);
    em.flush();
}
lloiacono
  • 4,714
  • 2
  • 30
  • 46
Subrata nath
  • 172
  • 10
0

Thanks to @Ashish451, the error lies in the fact that in my service code, I persisted department (d1). However, if I try to link employee (e1) and department (d1) first and then persist the employee, the JPA will not be happy because department (d1) has already been persisted and persist() only works on transient object or newly created instance.

Pupusa
  • 167
  • 1
  • 3
  • 14