当前位置: 首页 > 知识库问答 >
问题:

Hibernate 5 Java双向oneToMany字段为空,但表中包含数据

丁晋
2023-03-14

我有两个实体部门和员工。部门有一个员工列表。员工有一个部门字段。我可以创建员工并将他们添加到部门内部的列表中。数据库表在持久化时按预期填充。如果我查询部门,我得到部门,员工列表也被填充。这样一切都很好。如果我查询员工并得到部门字段,它会返回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;
...

--

查询员工所在的位置。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
            }
        }

表格似乎填满正确。但是如果我在查询的员工上使用getUnit,我就会得到null。如果我在查询的部门上使用get员工,我就会得到所有员工。

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

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

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

对我来说也是一样的结果。

我错过了什么?

这是完整的测试项目:更新的项目zip

已解决固定项目:已解决问题项目

共有3个答案

唐晗昱
2023-03-14

使用以下代码进行尝试,只需将联接表指向Employee中的Department即可。

@ManyToOne
@JoinColumn(name = "DPT_ID", insertable = false, updatable = false)
private Department department;
黄流觞
2023-03-14

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

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


@OneToMany(mappedBy = "department",cascade = CascadeType.ALL)
private List<Employee> employees = new ArrayList<>();
蒋斯伯
2023-03-14

看起来像是拥有实体的问题,所以我认为您的测试以两种不同的方式持久化数据。在您的注释中,您已声明部门为关系的所有者。因此,如果使用

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没有持久化,那么您将无法检索该部门。如果您让员工成为关系的所有者,效率会更高,因为它有外键。

@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
 类似资料:
  • null 注意-不存在,但这实际上是我试图测试的内容。这可以在AssertJ中实现吗?

  • 问题内容: 假设我有两个实体: 然后,我要保留“客户”实体,然后,参照先前添加的“客户”保留“订单”实体。当我从数据库中检索此客户并调用getOrders时,它将返回空集。这是正常行为吗?如果是,当我添加新的Order实体时,我该怎么做以自动刷新此集合? 问题答案: Jpa不会为您维护关系,因此应用程序需要设置双向关系的两端,以使它们与数据库保持同步。当您设置了order-> customer关系

  • GraphQL是否有可能让客户端告诉服务器,只有当某个字段不是时,它才需要该字段? 考虑到这个问题 然后,反应应该是这样的 而不是 这是否可能在不违反GraphQL规范的情况下实现?

  • 创建从到的OneToMany关系

  • 问题内容: 从golang规范 是否有使用内的 空白 字段的实际方案?(一些代码片段将不胜感激) 问题答案: 填充正是所谓的:一些填充,用于将以下字段与您的需求对齐,例如,匹配C结构的布局。无法访问它(至少在没有软件包不安全的情况下)。

  • 我们正试图在我们公司建立我们自己的表单-字段-组件。我们试图这样包装材料设计的组成部分: 字段: 文本框: 用法: 这大致会导致以下结果: 但是我在控制台中得到“mat-form-field必须包含一个MatFormFieldControl”。我想这与不直接包含matInput-field的mat-form-field有关。但它包含它,它只是在ng内容投影中。 这是一场闪电战:https://st