Reputation: 63
I am new to Hibernate and I encountered the classic "detached entity passed to persist" exception. I read a few similar questions here but non of them can apply to my situation.
I have 2 entities, Department and DeptEmpCode. Department has a foreign key that references DeptEmpCode. Many Departments may share one code, So it is many to one.
The entity code is as follows:
Department:
@Entity
@Table(name = "department")
public class Department {
private Integer id;
...
private DeptEmpCode status;
...
@ManyToOne(cascade=CascadeType.ALL)
@JoinColumn(name = "status")
public DeptEmpCode getStatus() {
return status;
}
public void setStatus(DeptEmpCode status) {
this.status = status;
}
}
DeptEmpCode:
@Entity
@Table(name = "code")
public class DeptEmpCode {
private Integer id;
....
}
I have omitted unnecessary code for easier read.
So when I hit the "add Department" button on the webpage, spring framework creates a Department object for me. Then I use "new" to create a DeptEmpCode object, and call Department's setStatus() to associate the DeptEmpCode object to the Department. Then I persist the Department. The code is as follows:
public void saveDept(Department dept) {
if(dept.getStatus() == null){
DeptEmpCode status = new DeptEmpCode();
status.setId(Constants.DEFAUL_DEPT_STATUS_ID);
dept.setStatus(status);
}
deptDAO.save(dept); //nothing but a persist() call
}
So what should be the problem? Should I make it bi-directional or its something else?(If I remove "cascadeType.ALL" then it would be a foreign key violation). Thanks!
Upvotes: 1
Views: 98
Reputation: 1527
since your Department object is already created. Use cascadeType.MERGE
instead of cascadeType.ALL
When you use cascadeType.ALL, it will think the transaction is PERSISTED, it tries to PERSIST Department as well and that doesn't work since Department already is in the db. But with CascadeType.MERGE the Department is automatically merged(update) instead.
Update: Service
@Override
@Transactional
public void saveDept(Department dept) {
if(dept.getStatus() == null){
DeptEmpCode status = new DeptEmpCode();
status.setId(Constants.DEFAUL_DEPT_STATUS_ID);
status.setType("DEFAULT");
status.setValue("DEFAULT");
dept.setStatus(status);
}
deptDAO.update(dept);
}
DAO
@Override
public void update(Department dept) {
em.merge(dept);
}
Explanation: since status.id is a primary key and you set the value by yourself. When you call save it will create the status first, so it means the status has already (detached object) register in the database(not yet commit), if you still use em.persist(dept), it will also try to persist the detach object (status) and the department. so we should use merge which will merge the status and insert a new department. you can see below is how hibernate insert your record.
however, if you don't set the status.id value and let it auto-generate, then it will persist department and status at the same time. so you won't have the problem. In your case, I know you want to assign the status.id to default which is one. So you should use merge as you will have the 2nd and 3rd department that use the default status id (which status id already in db).
Hibernate: insert into code (DES, INACTIVE_IND, CODE_TYPE, VALUE) values (?, ?, ?, ?)
Hibernate: insert into department (contact, des, dept_email, dept_name, status) values (?, ?, ?, ?, ?)
Upvotes: 1