【问题标题】:JPA Criteria API on ManyToOne optional relationshipManyToOne 可选关系上的 JPA Criteria API
【发布时间】:2017-08-24 03:34:11
【问题描述】:

我正在尝试使用 JPA Criteria API 对以下结构进行简单查询

1) 员工

    public class Employee {

        @Id
        @Column(name = "ID", length = 64)
        private String id;

        @Column(name = "NAME", length = 512)
        private String name;

        @ManyToOne(optional = true)
        @JoinColumn(name = "ORG_ID", nullable = true)
        private InternalOrg organization;

    }

2) 内部组织

    public class InternalOrg {

        @Id
        @Column(name = "ID", length = 64)
        private String id;

        @Column(name = "ORGANIZATION", length = 512)
        private String organization;

        @Column(name = "CODE", length = 64)
        private String code;

    }

3) 查询

    EntityManager em = getEntityManager();
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
    Root<Employee> emp = cq.from(Employee.class);

    cq.where(cb.or(emp.get(Employee_.organization).isNull(),
            cb.equal(emp.get(Employee_.organization).get(InternalOrg_.code), "1")));

    return em.createQuery(cq).getResultList();

正如您所见,Employee 的“组织”属性是可选的。我要做的是使用标准 API 进行查询,该 API 返回“employee.organization”为 NULL 或“employee.organization.code”等于参数的所有记录。我该如何进行?

我做了一些测试并意识到如果我改变这个:

    cq.where(cb.or(emp.get(Employee_.organization).isNull(),
            cb.equal(emp.get(Employee_.organization).get(InternalOrg_.code), "1")));

到这里:

 cq.where(cb.or(emp.get(Employee_.organization).isNull()));

它有效,但只返回组织为 NULL 的记录。

如果我改成这样:

cq.where(cb.equal(emp.get(Employee_.organization).get(InternalOrg_.code), "1"));

employee.organization 为 NULL 的记录将被忽略。

我如何返回组织满足条件的员工和组织为空的员工?

提前致谢,

【问题讨论】:

  • 请添加您的条件查询。
  • 你好@ZakiAnwarHamdani,刚刚更新了我的问题,提供了更多细节。

标签: java hibernate jpa


【解决方案1】:

终于找到了解决办法。

创建get期望结果的唯一方法是提前获取(JoinType.LEFT)关系,这是最终的条件查询:

    EntityManager em = getEntityManager();
    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
    Root<Employee> emp = cq.from(Employee.class);

    emp.fetch(Employee_.domain, JoinType.LEFT);

    cq.where(cb.or(emp.get(Employee_.organization).isNull(),
            cb.equal(emp.get(Employee_.organization).get(InternalOrg_.code), "1")));

    return em.createQuery(cq).getResultList();

感谢您的支持!

【讨论】:

  • 获取的域是什么?
  • Employee 实体中的一个字段
【解决方案2】:

通过调用 CriteriaQuery.where 方法设置的条件可以限制对 CriteriaQuery 对象的查询结果。调用 where 方法类似于在 JPQL 查询中设置 WHERE 子句。

例子:

EntityManager em = ...;
CriteriaBuilder cb = em.getCriteriaBuilder();    
CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
    Root<Employee> emp = cq.from(Employee.class);
    cq.where(emp.get(Employee_.organization).isNull());

要指定多个条件谓词,请使用 CriteriaBuilder 接口的复合谓词方法(和/或/非)。

cq.where(emp.get(Employee_.organization).isNull())
.or(cb.eq(emp.get(Employee_.organization.code), "ABC"));

更新:

试试这个:

cq.where(
  cb.or(
    cb.isNull(emp.get(Employee_.organization)),
    cb.equal(emp.get(Employee_.organization).get(InternalOrg_.code), "1")));

【讨论】:

  • 您好@Cam,是的,我正在按照您的建议进行操作。这里的问题是生成的查询确实检查了employee.organization = organization.id。因此,employee.organization 为 NULL 的记录不会被返回。
  • 现在我用一些额外的细节更新了问题。
猜你喜欢
  • 1970-01-01
  • 2023-03-15
  • 2014-11-01
  • 1970-01-01
  • 2020-02-18
  • 2015-08-14
  • 1970-01-01
  • 2018-12-24
  • 1970-01-01
相关资源
最近更新 更多