使用Criteria API与AND运算符进行LEFT JOIN

时间:2016-05-03 15:15:02

标签: sql jpa predicate criteria-api

我正在尝试使用JPA和Criteria API构建动态查询,但没有元模型。

使用表格:

Foo:
ID
1
2
3
4
Bar: 
ID   FooID   Number
1    2       44
2    2       55
3    3       55

我想检索所有Foo实体,其中没有匹配的Bar有44号。(期待Foo 1,3,4)

SQL应如下所示:

select distinct *
from Foo foo0_
left join Bar bar0_ on foo0_.ID=bar0_.FooID and bar0_.Number=44
where bar0__.id is null;

我的代码看起来像这样:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Foo> cq = cb.createQuery(Foo.class);
Root<Foo> fooRoot = cq.from(Foo.class);
cq.select(fooRoot).distinct(true);

List<Predicate> allPredicates = new ArrayList<Predicate>();
List<Predicate> orPredicates = new ArrayList<Predicate>();
List<Predicate> andPredicates = new ArrayList<Predicate>();
andPredicates.add(cb.equal(fooRoot.join("bars", JoinType.LEFT).get("number"), 44));
andPredicates.add(cb.isNull(fooRoot.join("bars", JoinType.LEFT).get("ID")));
orPredicates.add(cb.and(andPredicates.toArray(new Predicate[andPredicates.size()])));
allPredicates.add(cb.or(orPredicates.toArray(newPredicate[orPredicates.size()])));

cq.where(allPredicates.toArray(new Predicate[allPredicates.size()]));
TypedQuery<Foo> query = em.createQuery(cq);
query.getResultList();

但是这会生成这个SQL:

select distinct *
from Foo foo0_
left outer join Bar bar1_ on foo0_.id=bar1_.FooID
left outer join Bar bar2_ on foo0_.id=bar2_.FooID
where bar1_.Number=44 and (bar2_.id is null);

并且不返回Foo。

有什么想法吗?谢谢!

1 个答案:

答案 0 :(得分:2)

您没有指定这是针对哪个JPA实现,并且显然每个SQL可能会有所不同。

除此之外,你的加入是错误的。每个join调用都会执行JOIN,如果您编写的内容正确,则只需要1个连接。您还可以在连接上使用JPA 2.1“ON”条件(而不是将其放在WHERE中)。

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Foo> cq = cb.createQuery(Foo.class);
Root<Foo> fooRoot = cq.from(Foo.class);
cq.select(fooRoot).distinct(true);

Join barJoin = fooRoot.join("bars", JoinType.LEFT);
Predicate onCond = cb.equal(barJoin.get("number"), 44);
barJoin.on(onCond);

除了使用barJoin之外,请执行与您的问题相同的操作;不知道那些AND / OR条件试图做什么 - 你建议的SQL没有。