使用JPA CriteriaBuilder生成查询,其中属性在列表中或为空

时间:2014-08-28 17:43:58

标签: java jpa eclipselink criteria-api

我正在尝试使用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();
}

问题是此查询仅返回组在列表中的结果&#34; groups&#34;但由于某些原因,也不会返回组为空的结果(即连接表中没有条目)

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

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

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

&#34; TestContact&#34;中的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<List<SystemGroup>> groupPath = testContact.join("groups", JoinType.LEFT)上返回了以下错误

  

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

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

  

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

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

  

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

的有效替代品

1 个答案:

答案 0 :(得分:0)

testContact.get(“groups”);子句强制从testContact到组的内部联接,它过滤掉没有组的testContacts。您需要指定左外连接,并在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的例子