JPA 规范在获取时加入不起作用(Hibernate,Spring Data JPA)
JPA Specification join on fetch is not working (Hibernate, Spring Data JPA)
我正在使用带 spring-data-jpa 的 hibernate-orm。我有三个实体 A
、B
、C
声明如下:
@Entity
public class A {
@OneToMany(....)
private List<B> listOfB;
}
@Entity
public class B {
@ManyToOne(...)
private A a;
@OneToMany(...)
private List<C> listOfC;
}
@Entity
public class C {
@ManyToOne(...)
private B b;
}
我的 objective 是获取 A
并获取 listOfB
,但在实体 C
上有一些条件而不获取它。以下 JPQL 工作正常。
SELECT a FROM A a
LEFT JOIN FETCH a.listOfB b
LEFT JOIN b.listOfC c
WHERE c.xyz = :xyz
当我尝试使用 JPA 规范时,我的规范如下所示:
(rootA, query, builder) -> {
Fetch fetch = rootA.fetch(A_.listOfB, JoinType.LEFT);
ListJoin listJoin = ((ListJoin)fetch).join(B_.listOfC)
return builder.equal(listJoin.get(C_.xyz), xyz);
}
我正在重用提取操作完成的隐式连接。此连接在规范中不起作用。它正在输出以下 JPQL。
SELECT a FROM A a
LEFT JOIN FETCH a.listOfB b
WHERE c.xyz = :xyz
也就是说,没有 c
别名。
我查看了 Hibernate GitHub 源代码。我发现,有一个名为 QueryStructure.java
的 class 负责从条件对象生成 JPQL 查询。
我找到了呈现提取的函数 renderFetches。
@SuppressWarnings({ "unchecked" })
private void renderFetches(
StringBuilder jpaqlQuery,
RenderingContext renderingContext,
Collection<? extends Fetch> fetches) {
if ( fetches == null ) {
return;
}
for ( Fetch fetch : fetches ) {
( (FromImplementor) fetch ).prepareAlias( renderingContext );
jpaqlQuery.append( renderJoinType( fetch.getJoinType() ) )
.append( "fetch " )
.append( ( (FromImplementor) fetch ).renderTableExpression( renderingContext ) );
renderFetches( jpaqlQuery, renderingContext, fetch.getFetches() );
}
}
同样有一个函数renderJoins
负责所有的连接。
这两个是呈现条件对象树的递归函数。
很明显,提取中的所有连接都被忽略了。 renderFetches
内部没有调用函数 renderJoins
,这导致生成的查询不完整。
我们不从获取中加入内部有什么更深层次的原因吗?如果是,那么我如何重用 fetch 完成的现有隐式连接?
使用休眠测试用例模板重新生成此问题。
正如我在问题中提到的,renderFetches
内部没有 renderJoins
调用,在 renderFetches
末尾添加以下内容可解决问题。
if (fetch instanceof From) {
From from = (From) fetch;
renderJoins(jpaqlQuery, renderingContext, from.getJoins());
}
我给了一个PR HHH-14916 in the hibernate-orm.
合并 PR 将在下一个 5.6.x 版本.
中可用
我正在使用带 spring-data-jpa 的 hibernate-orm。我有三个实体 A
、B
、C
声明如下:
@Entity
public class A {
@OneToMany(....)
private List<B> listOfB;
}
@Entity
public class B {
@ManyToOne(...)
private A a;
@OneToMany(...)
private List<C> listOfC;
}
@Entity
public class C {
@ManyToOne(...)
private B b;
}
我的 objective 是获取 A
并获取 listOfB
,但在实体 C
上有一些条件而不获取它。以下 JPQL 工作正常。
SELECT a FROM A a
LEFT JOIN FETCH a.listOfB b
LEFT JOIN b.listOfC c
WHERE c.xyz = :xyz
当我尝试使用 JPA 规范时,我的规范如下所示:
(rootA, query, builder) -> {
Fetch fetch = rootA.fetch(A_.listOfB, JoinType.LEFT);
ListJoin listJoin = ((ListJoin)fetch).join(B_.listOfC)
return builder.equal(listJoin.get(C_.xyz), xyz);
}
我正在重用提取操作完成的隐式连接。此连接在规范中不起作用。它正在输出以下 JPQL。
SELECT a FROM A a
LEFT JOIN FETCH a.listOfB b
WHERE c.xyz = :xyz
也就是说,没有 c
别名。
我查看了 Hibernate GitHub 源代码。我发现,有一个名为 QueryStructure.java
的 class 负责从条件对象生成 JPQL 查询。
我找到了呈现提取的函数 renderFetches。
@SuppressWarnings({ "unchecked" })
private void renderFetches(
StringBuilder jpaqlQuery,
RenderingContext renderingContext,
Collection<? extends Fetch> fetches) {
if ( fetches == null ) {
return;
}
for ( Fetch fetch : fetches ) {
( (FromImplementor) fetch ).prepareAlias( renderingContext );
jpaqlQuery.append( renderJoinType( fetch.getJoinType() ) )
.append( "fetch " )
.append( ( (FromImplementor) fetch ).renderTableExpression( renderingContext ) );
renderFetches( jpaqlQuery, renderingContext, fetch.getFetches() );
}
}
同样有一个函数renderJoins
负责所有的连接。
这两个是呈现条件对象树的递归函数。
很明显,提取中的所有连接都被忽略了。 renderFetches
内部没有调用函数 renderJoins
,这导致生成的查询不完整。
我们不从获取中加入内部有什么更深层次的原因吗?如果是,那么我如何重用 fetch 完成的现有隐式连接?
使用休眠测试用例模板重新生成此问题。
正如我在问题中提到的,renderFetches
内部没有 renderJoins
调用,在 renderFetches
末尾添加以下内容可解决问题。
if (fetch instanceof From) {
From from = (From) fetch;
renderJoins(jpaqlQuery, renderingContext, from.getJoins());
}
我给了一个PR HHH-14916 in the hibernate-orm.
合并 PR 将在下一个 5.6.x 版本.
中可用