【问题标题】:Hibernate 5 Java bidirectional oneToMany field is null but table contains dataHibernate 5 Java双向oneToMany字段为空,但表包含数据
【发布时间】:2018-09-13 11:50:38
【问题描述】:

我有两个实体部门和员工。部门有一份员工名单。并且 Employee 有一个外勤部门。我可以创建员工并将他们添加到部门内的列表中。数据库表在持久化时按预期填充。如果我查询部门,我会得到部门并且员工列表已填写。这种方式一切都很好。如果我查询 Employee 并获取 Department 字段,则返回 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;
...

--

employee.getDepartment() 返回 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
            }
        }

表格似乎填写正确。但是,如果我在查询的员工上使用 getDepartment,我会得到空值。如果我在查询的部门上使用 getEmployees,我会得到所有员工。

我尝试了这里描述的两种方法:https://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch07.html#collections-bidirectional

示例 7.21。双向一对多,多对一作为关联所有者

示例 7.22。以一对多作为所有者的双向关联

对我来说同样的结果。

我错过了什么?

这里是完整的测试项目:updated Project zip

已解决的固定项目:SOLVED PROBLEM PROJECT

【问题讨论】:

  • 你应该在关系的子端使用mappedBy(不持有FK的那个)

标签: java hibernate one-to-many


【解决方案1】:

看起来像Owning entity 问题,所以我认为您的测试以两种不同的方式持久化数据。在您的注释@OneToMany(cascade = CascadeType.ALL) 中,您已将Department 声明为关系的所有者。所以,如果你用

持久化数据
dept.getEmployees().add(emp);

然后部门(id)字段将被更新

Hibernate: insert into EMPLOYEE (EMP_ID, DESIGNATION, NAME) values (null, ?, ?)
Hibernate: update EMPLOYEE set DEPARTMENT=? where EMP_ID=?

但如果你坚持

emp.setDepartment(dept);

那么员工的部门(id)字段将不会被更新。

Hibernate: insert into EMPLOYEE (EMP_ID, DESIGNATION, NAME) values (null, ?, ?)

如果员工的部门 id 未持久化,则无法检索部门。如果您将 Employee 设置为关系的所有者,则效率更高,因为它具有外键。

@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;

并在保持关系时设置员工的部门。然后插入是用 departmentid 完成的,而不是单独更新。

Hibernate: insert into EMPLOYEE (EMP_ID, DEPARTMENT, DESIGNATION, NAME) values (null, ?, ?, ?)

条件代码没有明显错误,因为 JPA 将遵循带注释的关系,但它在两个单独的查询中这样做,因为您没有特定的连接。

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=?

如果您添加特定的 Fetch,那么它将在单个 SQL 语句中完成。

root.fetch("department");

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

【讨论】:

  • 感谢您的详细解释。我创建了一个部门,而不是添加员工,而不是保留部门。 DEPARTMENT 列已正确更新。数据库已正确填充。但是当我查询员工时,我的部门仍然为空。但是,如果我查询部门,则会填写员工列表。我编辑问题以显示更多内容。
【解决方案2】:

在我看来,您的关系映射不正确!尝试像这样更改代码。

@ManyToOne
@JoinColumn(name = "DEPT_ID")
private Department department;


@OneToMany(mappedBy = "department",cascade = CascadeType.ALL)
private List<Employee> employees = new ArrayList<>();

【讨论】:

    【解决方案3】:

    用下面的代码试试,把连接表从Employee指向Department。

    @ManyToOne
    @JoinColumn(name = "DPT_ID", insertable = false, updatable = false)
    private Department department;
    

    【讨论】:

    • 如果我在 Employee 类中将名称从 DEPARTMENT 更改为 DPT_ID,hibernate 会在员工中生成一个额外的列,名称 DPT_ID 填充为空。
    【解决方案4】:

    我发现了问题。我使用相同的会话进行持久化和查询。

    session = HibernateUtil.getSessionFactory().openSession();
    transaction = session.getTransaction();
    transaction.begin();
    …
    session.persist(stuff);
    session.flush();
    transaction.commit();
    
    transaction = session.getTransaction();
    transaction.begin();
    
    query stuff
    

    如果我在持久化后关闭会话并打开一个新会话,一切正常。

    我在我的问题中添加了一个固定版本的测试项目,以防万一有人被迭代。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-08-16
      • 2013-05-09
      • 1970-01-01
      • 2023-03-04
      • 2016-05-16
      • 2016-07-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多