JPA Specification join on fetch is not working (Hibernate, Spring Data JPA)

Issue

I am using hibernate-orm with spring-data-jpa. I have three entities A, B, C declared as follows:

@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;
}

My objective is to get A and fetch listOfB as well, with some condition on entity C without fetching it. The following JPQL is working fine.

SELECT a FROM A a
LEFT JOIN FETCH a.listOfB b
LEFT JOIN b.listOfC c
WHERE c.xyz = :xyz

When I tried using JPA Specification my Specification it looks like the following:

(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);
}

I am reusing the implicit join done by the fetch operation. This join is not working in specification. It’s outputting the following JPQL.

SELECT a FROM A a
LEFT JOIN FETCH a.listOfB b
WHERE c.xyz = :xyz

Saying that, there is no c alias.

I have looked into Hibernate GitHub source code. I found out that, there is a class named QueryStructure.java responsible for generating JPQL query from the criteria object.

I found the function renderFetches which render the fetches.

@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() );
        }
    }

Similarly there is a function renderJoins responsible for all the joins.

These two are recursive functions rendering the criteria object tree.

It’s clear that all the joins inside the fetches are ignored. There is no call to function renderJoins inside from renderFetches which causes the generated query to be incomplete.

Is there any in depth reason why we are not joining inside from a fetch? If yes then how could I reuse the existing implicit joins done by fetch?

This issue is re-generated using hibernate test case template.

Solution

As i mentioned in my question, there is no renderJoins call from inside of renderFetches, adding following at the end of the renderFetches solves the issue.

if (fetch instanceof From) {
    From from = (From) fetch;
    renderJoins(jpaqlQuery, renderingContext, from.getJoins());
}

I have given a PR HHH-14916 in the hibernate-orm.

PR is merged will be available in the next 5.6.x release.

Answered By – Ratul Sharker

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published