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

Lai*_*son 12 jpa jpql

我认为我不会完全理解fetch连接.

我有一个查询,我试图热切地"夸大"两个级别的引用.

也就是说,我A有一个可选Collection的Bs,每个B都有0或1 C.B已知该系列的尺寸很小(10-20顶).我想预取这张图.

A的B关系标记为FetchType.LAZY并且是可选的. B与...的关系C也是可选的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
Run Code Online (Sandbox Code Playgroud)

当我运行它时,我看到确实已经获取了As B集合(我LEFT JOIN在SQL中引用了一个B映射到的表).

但是,我没有看到这样的证据表明该C表已被提取.

我怎样才能预取所有Cs和所有Bs,以及C从给定的"可达"的所有s A?我看不出有什么方法可以做到这一点.

Jam*_*mes 5

JPA规范不允许对提取连接进行别名,但是一些JPA提供程序会这样做.

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

通常,在提取连接上使用别名时需要小心,因为您可能会影响返回的数据.

请参阅 http://java-persistence-performance.blogspot.com/2012/04/objects-vs-data-and-filtering-join.html


小智 5

我正在使用 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
Run Code Online (Sandbox Code Playgroud)

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

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

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


Del*_*ris 5

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

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
Run Code Online (Sandbox Code Playgroud)

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