【问题标题】:Using JPA CriteriaBuilder to generate query where attribute is either in a list or is empty使用 JPA CriteriaBuilder 生成属性在列表中或为空的查询
【发布时间】:2014-08-28 17:43:57
【问题描述】:

我正在尝试使用 JPA CriteriaBuilder 为名为“TestContact”的实体生成查询,该实体与另一个名为“SystemGroup”的实体具有多对多连接,该连接的属性称为“组”。查询的目的是从“groups”属性在列表中或为空的“TestContact”实体中检索记录。

我使用的代码如下

public List<TestContact> findWithCriteriaQuery(List<SystemGroup> groups) {

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<TestContact> cq = cb.createQuery(TestContact.class);
    Root<TestContact> testContact = cq.from(TestContact.class);
    cq.select(testContact);

    Path<List<SystemGroup>> groupPath = testContact.get("groups");

    // cq.where(groupPath.in(groups));
    // cq.where(cb.isEmpty(groupPath));
    cq.where(cb.or(cb.isEmpty(groupPath), groupPath.in(groups)));

    TypedQuery<TestContact> tq = em.createQuery(cq);

    return tq.getResultList();
}

问题是这个查询只返回组在列表“组”中的结果,但由于某种原因,它也没有返回组为空的结果(即连接表中没有条目)

如果我将 where 子句更改为 cq.where(cb.isEmpty(groupPath));,那么查询会正确返回 group 为空的结果。

如果我将 where 子句更改为 cq.where(groupPath.in(groups));,则查询会正确返回组在列表“组”中的结果。

我不明白为什么当我尝试使用 CriteriaBuilder 或方法组合这两个谓词时,结果不包括组在列表中或为空的记录。

“TestContact”实体中的groups属性声明如下

@ManyToMany(fetch=FetchType.EAGER)
@JoinTable(name = "TEST_CONTACT_GROUPS", joinColumns = { @JoinColumn(name = "CONTACT_ID", referencedColumnName = "CONTACT_ID") }, inverseJoinColumns = { @JoinColumn(name = "GROUP_ID", referencedColumnName = "GROUP_ID") })
private List<SystemGroup> groups;

JPA 提供程序是 EclipseLink 2.5.0,Java EE 应用服务器是 GlassFish 4,数据库是 Oracle 11gR2。

谁能指出我哪里出错了?

更新

我尝试了@Chris 的建议,但 Eclipse 在Join&lt;List&lt;SystemGroup&gt;&gt; groupPath = testContact.join("groups", JoinType.LEFT) 上返回以下错误

类型 Join 的参数数量不正确;它不可能是 参数化参数 >

查看Join 的JavaDoc,它说类型参数是......

Z - 连接的源类型,X - 连接的目标类型

我尝试了Join&lt;TestContact, SystemGroup&gt; groupPath = testContact.join("groups", JoinType.LEFT);,然后导致 Eclipse 在cb.isEmpty 上返回以下错误

绑定不匹配:类型的泛型方法isEmpty(Expression) CriteriaBuilder 不适用于参数 (加入)。推断的类型 SystemGroup 不是 有界参数的有效替代>

【问题讨论】:

    标签: java jpa eclipselink criteria-api


    【解决方案1】:

    testContact.get("groups");子句强制从 testContact 到组的内部连接,这会过滤掉没有组的 testContact。您需要指定一个左外连接,并在您的 isEmpty 和 in 子句中使用它。

    Root<TestContact> testContact = cq.from(TestContact.class);
    cq.select(testContact);
    
    Join<TestContact, SystemGroup> groupPath = testContact.join("groups", JoinType.LEFT);
    cq.where(cb.or(cb.isEmpty(testContact.get("groups")), groupPath.in(groups)));
    

    例子我一般参考https://en.wikibooks.org/wiki/Java_Persistence/Criteria#Join

    【讨论】:

    • 谢谢 - 我已经尝试过您的建议,但在 Join&lt;List&lt;SystemGroup&gt;&gt; groupPath = testContact.join("groups", JoinType.LEFT); 上遇到错误。我已经用一些进一步的细节更新了原始问题。有什么想法吗?
    • 我没有一个测试项目可以先试用它。我已经更新了现在应该可以工作的示例 - 我相信 JPA 应该使用 .get("groups") 创建一个子查询,同时外部连接到 'in' 子句的关系。
    • Eclipse 报告 .isEmpty() 不是 testContact.get("groups") 的方法。 Eclipse 建议将其转换为 CriteriaBuilder,但如果我尝试这样做,那么 Eclipse 会在 .isEmpty() 方法上出现“绑定不匹配错误”。
    • 我最后使用的代码是Root&lt;TestContact&gt; testContact = cq.from(TestContact.class); cq.select(testContact); Path&lt;List&lt;SystemGroup&gt;&gt; groupPath1 = testContact.get("groups"); ListJoin&lt;TestContact, SystemGroup&gt; groupPath2 = testContact.joinList("groups",JoinType.LEFT); cq.where(cb.or(cb.isEmpty(groupPath1),groupPath2.in(groups)));
    猜你喜欢
    • 2017-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-27
    • 1970-01-01
    • 2014-03-28
    • 1970-01-01
    相关资源
    最近更新 更多