如何在 JPQL 中进行“深度”获取连接?

Posted

技术标签:

【中文标题】如何在 JPQL 中进行“深度”获取连接?【英文标题】:How do I do a "deep" fetch join in JPQL? 【发布时间】:2013-05-16 20:20:00 【问题描述】:

我认为我永远不会完全理解 fetch joins。

我有一个查询,我正试图急切地将引用“膨胀”到两个级别。

也就是说,我的A 有一个可选的Collection Bs,每个B 有0 或1 个C。众所周知,B 集合的大小很小(10-20 个顶部)。我想预取这张图。

AB 关系标记为FetchType.LAZY 并且是可选的。 BC 的关系也是可选的,FetchType.LAZY

我希望我能做到:

SELECT a 
  FROM A a
  LEFT JOIN FETCH a.bs // look, no alias; JPQL forbids it
  LEFT JOIN a.bs b // "repeated" join necessary since you can't alias fetch joins
  LEFT JOIN FETCH b.c // this doesn't seem to do anything
 WHERE a.id = :id

当我运行它时,我看到确实提取了 As B 集合(我在 SQL 中看到 LEFT JOIN 引用了 B 映射到的表)。

但是,我没有看到C 的表已被提取的此类证据。

如何从给定的A 预取所有“可访问”的Cs 和所有Bs 和所有Cs?我看不出有什么办法。

【问题讨论】:

您使用什么 JPA 提供程序? EclipseLink 中有专门针对此类功能的提示。查看eclipselink.join-fetcheclipselink.batch 提示... 谢谢。如果我使用eclipselink.join-fetch,看起来我只能设置一个属性。那是对的吗?例如,如果我将我的join-fetch 大写于a.bs.c,那么如果我还想加入 fetch——比如说——a.bs.d,那我就不走运了。对吗? 哦,这很有趣。 EclipseLink 文档 (wiki.eclipse.org/EclipseLink/UserGuide/JPA/…) 并没有说重复的查询提示键是可能的,但也许它们是?看到这个有趣的链接:github.com/mysema/querydsl/issues/348 我可以验证多个eclipselink.join-fetch 提示是否有效。我们在工作中大量使用它,生成的 SQL 语句是正确的。 谢谢。你有没有遇到过这个错误:bugs.eclipse.org/bugs/show_bug.cgi?id=408719? 【参考方案1】:

JPA 规范不允许为 fetch 连接设置别名,但一些 JPA 提供程序允许。

EclipseLink 从 2.4 开始。 EclipseLink 还允许使用点表示法进行嵌套连接获取(即“JOIN FETCH a.bs.c”),并支持允许嵌套连接的查询提示“eclipselink.join-fetch”(您可以指定多个相同提示名称的提示)。

一般来说,在 fetch 连接上使用别名时需要小心,因为这会影响返回的数据。

看, http://java-persistence-performance.blogspot.com/2012/04/objects-vs-data-and-filtering-join.html

【讨论】:

谢谢,@James;由于各种原因,我们需要保持我们的 JPQL 标准,所以它看起来像(字符串格式)查询提示。值得注意的是,使用其中一些会导致错误:bugs.eclipse.org/bugs/show_bug.cgi?id=408719(其他人在 2010 年在这里也报告过:forums.oracle.com/forums/thread.jspa?threadID=847970。无论如何,我对预取太深持怀疑态度,所以也许这奇怪的是最好的。: -) eclipselink.join-fetch 也在 eclipseLink 2.3 中工作:.setHint("eclipselink.join-fetch", "a.bs.c")【参考方案2】:

我正在使用 Hibernate(这可能是特定于它的)并且我已经取得了成功:

SELECT DISTINCT a, b 
FROM A a
LEFT JOIN a.bs b
LEFT JOIN FETCH a.bs
LEFT JOIN FETCH b.c
WHERE a.id = :id

(注意选择列表中的b)。

这是我发现这对我有用的唯一方法,请注意,这会为我返回 Object[],然后我在代码中过滤它,如下所示:

(List<A>) q.getResultList().stream().map(pair -> (A) (((Object[])pair)[0])).distinct().collect(Collectors.toList());

【讨论】:

【参考方案3】:

JPA 不允许嵌套连接提取,也不允许连接上的别名 fetch,所以这可能是特定于 JPA 提供程序的。

在 EclipseLink 中,您可以指定查询提示来执行嵌套连接 获取。

你不能在 JPQL 中让它递归,你最多只能去 n 个级别。在 EclipseLink 中,您可以使用 @JoinFetch 或 @BatchFetch 使查询递归的映射。

看, http://java-persistence-performance.blogspot.com/2010/08/batch-fetching-optimizing-object-graph.html

来源:http://www.coderanch.com/t/570828/ORM/databases/Recursive-fetch-join-recursively-fetching

【讨论】:

【参考方案4】:

不完全是 JPQL,但您可以在纯 JPA 中使用 Criteria 查询来实现:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<MyEntity> q = cb.createQuery(MyEntity.class);
Root<MyEntity> root = q.from(MyEntity.class);
q.select(root);
Fetch bsFetch = root.fetch("b", JoinType.LEFT); //fetch b, property of MyEntity and hold fetch object
bsFetch.fetch("c", JoinType.LEFT); //fetch c, property of b

对这种嵌套获取的支持是特定于供应商的(因为 JPA 不要求他们这样做),但 eclipselink 和 hibernate 都支持它,这样您的代码就保持与供应商无关。

【讨论】:

以上是关于如何在 JPQL 中进行“深度”获取连接?的主要内容,如果未能解决你的问题,请参考以下文章

如何编写一个 JPQL 查询来查找此连接中未找到的记录?

JPQL:多重连接。如何做我的命名查询?

如何使用 JPQL 从 SELECT 操作中获取第一行?

如何使用 JPA 1.0 构建一个 JPQL 查询,从多个表中获取数据以克服延迟初始化?

如何在jpql中对其进行排序?

如何在 JPQL 查询中从 LocalDate 获取月份?