Hibernate 5 Java bidirectional oneToMany field is null but table contains data

允我心安 提交于 2021-02-10 14:51:55

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!