问题
I have two entities Department and Employee. Department has a list of Employees. And Employee has a field Department. I can create Employees and add them to to list inside Department. The Database Tables are filled as expected on persist. If I query for a Department I get the Department and the List of Employees is filled. Everything fine this way. If I query for an Employee and get the Department field it Returns null.
@Entity
@Table(name = "DEPARTMENT")
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "DPT_ID")
private long id;
@Column(name = "NAME", nullable = false, unique = true)
private String name;
@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "DEPARTMENT") //we need to duplicate the physical information
private List<Employee> employees = new ArrayList<>();
…
--
@Entity
@Table(name = "EMPLOYEE")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "EMP_ID")
private long id;
@Column(name = "NAME", nullable = false)
private String name;
@Column(name = "DESIGNATION")
private String designation;
@ManyToOne
@JoinColumn(name = "DEPARTMENT", insertable = false, updatable = false)
private Department department;
...
--
The query where employee.getDepartment() return null
session = HibernateUtil.getSessionFactory().openSession();
transaction = session.getTransaction();
transaction.begin();
Department department = new Department();
department.setName("IT Department");
Employee employee1 = new Employee();
employee1.setName("Adam");
employee1.setDesignation("Manager");
Employee employee2 = new Employee();
employee2.setName("Miller");
employee2.setDesignation("Software Engineer");
Employee employee3 = new Employee();
employee3.setName("Smith");
employee3.setDesignation("Associate Engineer");
department.getEmployees().add(employee1);
department.getEmployees().add(employee2);
department.getEmployees().add(employee3);
session.persist(department);
session.flush();
transaction.commit();
transaction = session.getTransaction();
transaction.begin();
{
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Employee> query = builder.createQuery(Employee.class);
Root<Employee> root = query.from(Employee.class);
query.select(root);
Query<Employee> q = session.createQuery(query);
List<Employee> employees = q.getResultList();
for (Employee employee : employees) {
System.out.println("EMPLOYEE NAME: " + employee.getName());
System.out.println("DEPARTMENT NAME: " + employee.getDepartment()); // gives null
}
}
{
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Department> query = builder.createQuery(Department.class);
Root<Department> root = query.from(Department.class);
query.select(root);
Query<Department> q = session.createQuery(query);
List<Department> departments = q.getResultList();
for (Department deps : departments) {
System.out.println(deps.getName());
System.out.println(deps.getEmployees()); // list of employees is filled
}
}
The tables seem to be filled correctly. But if i use getDepartment on a queried Employee i get null. If i use getEmployees on a queried Department i get all the Employees.
I tried both ways described here : https://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch07.html#collections-bidirectional
Example 7.21. Bidirectional one to many with many to one side as association owner
and
Example 7.22. Bidirectional association with one to many side as owner
Same result for me.
What am i missing?
Here is the complete test Project: updated Project zip
SOLVED FIXED PROJECT: SOLVED PROBLEM PROJECT
回答1:
Looks like a Owning entity
issue, so I think your test is persisting data in two different ways. In your annotation @OneToMany(cascade = CascadeType.ALL)
you have declared Department
to be the owner of the relationship. So, if you persist data with
dept.getEmployees().add(emp);
then the department (id) field will be updated
Hibernate: insert into EMPLOYEE (EMP_ID, DESIGNATION, NAME) values (null, ?, ?)
Hibernate: update EMPLOYEE set DEPARTMENT=? where EMP_ID=?
but if you persist with
emp.setDepartment(dept);
then the department(id) field of employee won't get updated.
Hibernate: insert into EMPLOYEE (EMP_ID, DESIGNATION, NAME) values (null, ?, ?)
If the department id of employee isn't persisted then you can't retrieve the department. It's more efficient if you make Employee the owner of the relationship since it has the foreign key.
@OneToMany(cascade = CascadeType.ALL, mappedBy="department")
private List<Employee> employees; // don't need to make a list, only for fetches
// and
@ManyToOne
@JoinColumn(name = "DEPARTMENT")
private Department department;
and set the department of the employee when persisting relations. Then the insert is done with the departmentid and not updated separately.
Hibernate: insert into EMPLOYEE (EMP_ID, DEPARTMENT, DESIGNATION, NAME) values (null, ?, ?, ?)
There is nothing expressly wrong with the criteria code as JPA will follow the annotated relationship but it does so in two separate queries since you don't have a specific join.
Hibernate: select employee0_.EMP_ID as EMP_ID1_1_, employee0_.DEPARTMENT as DEPARTME4_1_, employee0_.DESIGNATION as DESIGNAT2_1_, employee0_.NAME as NAME3_1_ from EMPLOYEE employee0_
Hibernate: select department0_.DPT_ID as DPT_ID1_0_0_, department0_.NAME as NAME2_0_0_ from DEPARTMENT department0_ where department0_.DPT_ID=?
If you add a specific Fetch then it will do it in a single SQL statement.
root.fetch("department");
and
Hibernate: select employee0_.EMP_ID as EMP_ID1_1_0_, department1_.DPT_ID as DPT_ID1_0_1_, employee0_.DEPARTMENT as DEPARTME4_1_0_, employee0_.DESIGNATION as DESIGNAT2_1_0_, employee0_.NAME as NAME3_1_0_, department1_.NAME as NAME2_0_1_ from EMPLOYEE employee0_ inner join DEPARTMENT department1_ on employee0_.DEPARTMENT=department1_.DPT_ID
回答2:
In my opinion, your relational mapping is incorrect! Try to change code like this.
@ManyToOne
@JoinColumn(name = "DEPT_ID")
private Department department;
@OneToMany(mappedBy = "department",cascade = CascadeType.ALL)
private List<Employee> employees = new ArrayList<>();
回答3:
try it with following code , just point the join table to Department from Employee.
@ManyToOne
@JoinColumn(name = "DPT_ID", insertable = false, updatable = false)
private Department department;
回答4:
I found the Problem. I used the same session for persiting and querying.
session = HibernateUtil.getSessionFactory().openSession();
transaction = session.getTransaction();
transaction.begin();
…
session.persist(stuff);
session.flush();
transaction.commit();
transaction = session.getTransaction();
transaction.begin();
query stuff
If i close the session after persist and open a new session everything is working fine.
I added a fixed version of the test project to my question just i case someone is iterested.
来源:https://stackoverflow.com/questions/52313118/hibernate-5-java-bidirectional-onetomany-field-is-null-but-table-contains-data