如何使用“where”子句将 JPQL“join fetch”正确表达为 JPA 2 CriteriaQuery?

新手上路,请多包涵

考虑以下 JPQL 查询:

 SELECT foo FROM Foo foo
INNER JOIN FETCH foo.bar bar
WHERE bar.baz = :baz

我正在尝试将其转换为 Criteria 查询。据我所知:

 CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Foo> cq = cb.createQuery(Foo.class);
Root<Foo> r = cq.from(Foo.class);
Fetch<Foo, Bar> fetch = r.fetch(Foo_.bar, JoinType.INNER);
Join<Foo, Bar> join = r.join(Foo_.bar, JoinType.INNER);
cq.where(cb.equal(join.get(Bar_.baz), value);

这里明显的问题是我做了两次相同的连接,因为 Fetch<Foo, Bar> 似乎没有办法获得 Path 。有什么办法可以避免必须加入两次?或者我必须坚持使用这么简单的查询的旧 JPQL 吗?

原文由 chris 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 527
2 个回答

在 JPQL 中,规范中实际上也是如此。 JPA 规范不允许为获取连接指定别名。问题在于,通过限制连接获取的上下文,您可以很容易地搬起石头砸自己的脚。加入两次更安全。

这通常是 ToMany 比 ToOnes 更严重的问题。例如,

 Select e from Employee e
join fetch e.phones p
where p.areaCode = '613'

这将 错误地 返回所有包含“613”区号中的号码的员工,但会在返回列表中遗漏其他地区的电话号码。这意味着拥有 613 和 416 区号电话的员工将丢失 416 电话号码,因此对象将被损坏。

当然,如果您知道自己在做什么,那么额外的连接是不可取的,一些 JPA 提供程序可能允许别名连接获取,并且可能允许将 Criteria Fetch 转换为连接。

原文由 James 发布,翻译遵循 CC BY-SA 3.0 许可协议

我将使用 James answer 中的优秀示例并添加替代解决方案来直观地展示问题。

当您执行以下查询时,没有 FETCH

 Select e from Employee e
join e.phones p
where p.areaCode = '613'

如您所料,您将从 Employee 获得以下结果:

员工ID员工姓名电话号码电话区号1个詹姆士5个613 1个詹姆士6个416

但是,当您在 JOIN ( FETCH JOIN ) 上添加 FETCH 子句时,会发生以下情况:

员工ID员工姓名电话号码电话区号1个詹姆士5个613

这两个查询生成的 SQL 是相同的,但是 Hibernate 在内存 中删除了 416 注册时使用 WHEREFETCH

So, to bring all phones and apply the WHERE correctly, you need to have two JOIN s: one for the WHERE and another for the FETCH 。喜欢:

 Select e from Employee e
join e.phones p
join fetch e.phones      //no alias, to not commit the mistake
where p.areaCode = '613'

原文由 Dherik 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题