tags:

views:

424

answers:

3

While I try to save top level entity (using JPA), do I need to get the ManyToOne mapped entity freshly from database and set it or cannot I just set Id (of manyToOne mapped entity and save top level entity? When do not get fresh entity it throws: org.hibernate.TransientObjectException:

Table structures we are using:

DEPARTMENT(DEPARTMENT_ID BIGINT, NAME VARCHAR(128))

EMPLOYEE(EMPLOYEE_ID BIGINT, NAME VARCHAR(128), DEPARTMENT_ID BIGINT)

Entities:
class Department
{
   @Id
   Long departmentId;
   String name;
   @Version
   Long versionNumber;
}

class Employee
{
   @Id
   Long employeeId;
   String name;
   @ManyToOne
   Department department;
   @Version
   Long versionNumber
}

(both classes have setter and getter methods for all fields and default constructor, constructor which takes primary key as argument) Now if I want to save Employee with departmentId (say 100), do I need to get the Department record first and then set it in employee?

Cannot I create instance of Department directly (by setting primary key(departmentId)) and set Department instance in Employee and save Employee? When I do this it is throwing org.hibernate.TransientObjectException.

Any suggestions on best practice to be followed for this?

Thank you in advance

+1  A: 

If you want to save the parent without saving each of the children, you need to map it something like (I can't remember the exact syntax)

@ManyToOne(CascadeType = Cascade.All)
Department department;

EDIT: I made the mistake of seeing it as parent-to-child instead of child-to-parent. Follow ChssPly76's example.

Wysawyg
+1  A: 

If you want to associate your Employee instance with new `Department instance, you can set an appropriate cascading behavior and let Hibernate take care of the whole thing:

class Employee {
  ...
  @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
  Department department;
}

Note that you do NOT want to use CascadeType.ALL because deleting an employee should not delete a department.

If, OTOH, you want to associate your Employee instance with existing `Department instance, your best approach is indeed to load it. You can use session.load() method for that purpose which will not hit the database.

An alternative solution is to use session.merge() on Employee which (with cascading as specified above) will propagate to Department. This can have side effects, though.

ChssPly76
Ah, my bad, I missed in typing that it was @ManyToOne instead of vice versa. Yes, Cascade.All would be bad!
Wysawyg
Thanks. My requirement is little different. I do not want to hit the database at all.
Vineyard
As I said above, use `session.load()` - it will not hit the database at all up until you flush / commit.
ChssPly76
A: 

Thank you ChssPly76 and Wysawyg.

One of the solution could be: We will update Employee POJO as below

ManyToOne(fetch=FetchType.EAGER)
@**JoinColumn**(name = "DEPARTMENT_ID", referencedColumnName = "DEPARTMENT_ID", **insertable=false, updatable=false**)
private Department department;

@Column(name = "department_id")
private Long departmentId;

(both department and departmentId will have setter and getter methods)

and Now here (please see that both department and departmentId mapped to same column (DEPARTMENT_ID) we use department only for fetching Department details and departmentId to insert or update Employee

But I am worried if it is a better approach.

Vineyard