HHH-17777 Reuse existing fetch-joins if requesting the same join type
This commit is contained in:
parent
b3955d7d75
commit
3ce10df785
|
@ -30,6 +30,8 @@ import org.hibernate.query.sqm.tree.from.SqmRoot;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.hibernate.query.sqm.internal.SqmUtil.findCompatibleFetchJoin;
|
||||
|
||||
/**
|
||||
* Specialized "intermediate" SemanticPathPart for processing domain model paths.
|
||||
*
|
||||
|
@ -191,15 +193,26 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
|
|||
name,
|
||||
creationState.getCreationContext().getJpaMetamodel()
|
||||
);
|
||||
if ( allowReuse && !isTerminal ) {
|
||||
for ( SqmJoin<?, ?> sqmJoin : lhs.getSqmJoins() ) {
|
||||
if ( sqmJoin.getAlias() == null && sqmJoin.getReferencedPathSource() == subPathSource ) {
|
||||
return sqmJoin;
|
||||
if ( allowReuse ) {
|
||||
if ( !isTerminal ) {
|
||||
for ( SqmJoin<?, ?> sqmJoin : lhs.getSqmJoins() ) {
|
||||
if ( sqmJoin.getAlias() == null && sqmJoin.getReferencedPathSource() == subPathSource ) {
|
||||
return sqmJoin;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( fetch ) {
|
||||
final SqmAttributeJoin<U, ?> compatibleFetchJoin = findCompatibleFetchJoin( lhs, subPathSource, joinType );
|
||||
if ( compatibleFetchJoin != null ) {
|
||||
if ( alias != null ) {
|
||||
throw new IllegalStateException( "Cannot fetch the same association twice with a different alias" );
|
||||
}
|
||||
return compatibleFetchJoin;
|
||||
}
|
||||
}
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
SqmJoinable<U, ?> joinSource = (SqmJoinable<U, ?>) subPathSource;
|
||||
final SqmJoinable<U, ?> joinSource = (SqmJoinable<U, ?>) subPathSource;
|
||||
return createJoin( lhs, joinType, alias, fetch, isTerminal, allowReuse, creationState, joinSource );
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.hibernate.query.spi.QueryParameterBinding;
|
|||
import org.hibernate.query.spi.QueryParameterBindings;
|
||||
import org.hibernate.query.spi.QueryParameterImplementor;
|
||||
import org.hibernate.query.sqm.NodeBuilder;
|
||||
import org.hibernate.query.sqm.SqmPathSource;
|
||||
import org.hibernate.query.sqm.SqmQuerySource;
|
||||
import org.hibernate.query.sqm.spi.JdbcParameterBySqmParameterAccess;
|
||||
import org.hibernate.query.sqm.spi.SqmParameterMappingModelResolutionAccess;
|
||||
|
@ -53,6 +54,7 @@ import org.hibernate.query.sqm.tree.expression.SqmAliasedNodeRef;
|
|||
import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmParameter;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmTuple;
|
||||
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
|
||||
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||
import org.hibernate.query.sqm.tree.from.SqmJoin;
|
||||
import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin;
|
||||
|
@ -159,6 +161,32 @@ public class SqmUtil {
|
|||
return false;
|
||||
}
|
||||
|
||||
public static <T, A> SqmAttributeJoin<T, A> findCompatibleFetchJoin(
|
||||
SqmFrom<?, T> sqmFrom,
|
||||
SqmPathSource<A> pathSource,
|
||||
SqmJoinType requestedJoinType) {
|
||||
for ( final SqmJoin<T, ?> join : sqmFrom.getSqmJoins() ) {
|
||||
if ( join.getReferencedPathSource() == pathSource ) {
|
||||
final SqmAttributeJoin<T, ?> attributeJoin = (SqmAttributeJoin<T, ?>) join;
|
||||
if ( attributeJoin.isFetched() ) {
|
||||
final SqmJoinType joinType = join.getSqmJoinType();
|
||||
if ( joinType != requestedJoinType ) {
|
||||
throw new IllegalStateException( String.format(
|
||||
"Requested join fetch with association [%s] with '%s' join type, " +
|
||||
"but found existing join fetch with '%s' join type.",
|
||||
pathSource.getPathName(),
|
||||
requestedJoinType,
|
||||
joinType
|
||||
) );
|
||||
}
|
||||
//noinspection unchecked
|
||||
return (SqmAttributeJoin<T, A>) attributeJoin;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Map<QueryParameterImplementor<?>, Map<SqmParameter<?>, List<JdbcParametersList>>> generateJdbcParamsXref(
|
||||
DomainParameterXref domainParameterXref,
|
||||
JdbcParameterBySqmParameterAccess jdbcParameterBySqmParameterAccess) {
|
||||
|
|
|
@ -61,6 +61,8 @@ import jakarta.persistence.metamodel.PluralAttribute;
|
|||
import jakarta.persistence.metamodel.SetAttribute;
|
||||
import jakarta.persistence.metamodel.SingularAttribute;
|
||||
|
||||
import static org.hibernate.query.sqm.internal.SqmUtil.findCompatibleFetchJoin;
|
||||
|
||||
/**
|
||||
* Convenience base class for SqmFrom implementations
|
||||
*
|
||||
|
@ -670,8 +672,18 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
|
|||
|
||||
@Override
|
||||
public <A> SqmSingularJoin<T, A> fetch(SingularAttribute<? super T, A> attribute, JoinType jt) {
|
||||
final SingularPersistentAttribute<? super T, A> persistentAttribute = (SingularPersistentAttribute<? super T, A>) attribute;
|
||||
final SqmAttributeJoin<T, A> compatibleFetchJoin = findCompatibleFetchJoin(
|
||||
this,
|
||||
persistentAttribute,
|
||||
SqmJoinType.from( jt )
|
||||
);
|
||||
if ( compatibleFetchJoin != null ) {
|
||||
return (SqmSingularJoin<T, A>) compatibleFetchJoin;
|
||||
}
|
||||
|
||||
final SqmSingularJoin<T, A> join = buildSingularJoin(
|
||||
(SingularPersistentAttribute<? super T, A>) attribute,
|
||||
persistentAttribute,
|
||||
SqmJoinType.from( jt ),
|
||||
true
|
||||
);
|
||||
|
@ -714,6 +726,11 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
|
|||
SqmPathSource<A> joinedPathSource,
|
||||
SqmJoinType joinType,
|
||||
boolean fetched) {
|
||||
final SqmAttributeJoin<T, A> compatibleFetchJoin = findCompatibleFetchJoin( this, joinedPathSource, joinType );
|
||||
if ( compatibleFetchJoin != null ) {
|
||||
return compatibleFetchJoin;
|
||||
}
|
||||
|
||||
final SqmAttributeJoin<T, A> sqmJoin;
|
||||
if ( joinedPathSource instanceof SingularPersistentAttribute<?, ?> ) {
|
||||
sqmJoin = buildSingularJoin(
|
||||
|
|
|
@ -93,15 +93,9 @@ public class OneToManyBidirectionalTest {
|
|||
List<Item> items = session.createQuery(
|
||||
"from Item i" +
|
||||
" join fetch i.order o" +
|
||||
" join fetch i.order o2", Item.class ).list();
|
||||
/*
|
||||
select i1_0.id, o21_0.id, o21_0.name
|
||||
from Item as i1_0
|
||||
inner join "Order" as o21_0 on i1_0."order_id" = o21_0.id
|
||||
inner join "Order" as o1_0 on i1_0."order_id" = o1_0.id
|
||||
*/
|
||||
" join fetch i.order", Item.class ).list();
|
||||
|
||||
sqlStatementInterceptor.assertNumberOfJoins( 0, SqlAstJoinType.INNER, 2 );
|
||||
sqlStatementInterceptor.assertNumberOfJoins( 0, SqlAstJoinType.INNER, 1 );
|
||||
sqlStatementInterceptor.clear();
|
||||
|
||||
assertThat( items.size(), is( 2 ) );
|
||||
|
|
Loading…
Reference in New Issue