fix bug in query plan cache where fetch profiles were not considered

This commit is contained in:
Gavin 2023-05-23 12:13:30 +02:00 committed by Gavin King
parent ba0221da36
commit b3e27788fa
1 changed files with 40 additions and 48 deletions

View File

@ -6,6 +6,8 @@
*/
package org.hibernate.query.sqm.internal;
import java.util.Collection;
import java.util.HashSet;
import java.util.function.Supplier;
import org.hibernate.LockOptions;
@ -16,6 +18,7 @@ import org.hibernate.query.spi.QueryInterpretationCache;
import org.hibernate.query.spi.QueryOptions;
import static java.lang.Boolean.TRUE;
import static org.hibernate.query.spi.AbstractSelectionQuery.CRITERIA_HQL_STRING;
/**
* @author Steve Ebersole
@ -33,49 +36,39 @@ public class SqmInterpretationsKey implements QueryInterpretationCache.Key {
}
public static SqmInterpretationsKey createInterpretationsKey(InterpretationsKeySource keySource) {
if ( ! isCacheable( keySource ) ) {
if ( isCacheable (keySource ) ) {
return new SqmInterpretationsKey(
keySource.getQueryString(),
keySource.getResultType(),
keySource.getQueryOptions().getLockOptions(),
keySource.getQueryOptions().getTupleTransformer(),
keySource.getQueryOptions().getResultListTransformer(),
new HashSet<>( keySource.getLoadQueryInfluencers().getEnabledFetchProfileNames() )
);
}
else {
return null;
}
return new SqmInterpretationsKey(
keySource.getQueryString(),
keySource.getResultType(),
keySource.getQueryOptions().getLockOptions(),
keySource.getQueryOptions().getTupleTransformer(),
keySource.getQueryOptions().getResultListTransformer()
);
}
@SuppressWarnings("RedundantIfStatement")
private static boolean isCacheable(InterpretationsKeySource keySource) {
assert keySource.getQueryOptions().getAppliedGraph() != null;
if ( QuerySqmImpl.CRITERIA_HQL_STRING.equals( keySource.getQueryString() ) ) {
// for now at least, skip caching Criteria-based plans
// - especially wrt parameters atm; this works with HQL because the parameters
// are part of the query string; with Criteria, they are not.
return false;
}
if ( keySource.getLoadQueryInfluencers().hasEnabledFilters() ) {
// At the moment we cannot cache query plan if there is filter enabled.
return false;
}
if ( keySource.getQueryOptions().getAppliedGraph().getSemantic() != null ) {
// At the moment we cannot cache query plan if there is an
// EntityGraph enabled.
return false;
}
if ( keySource.hasMultiValuedParameterBindingsChecker().get() == TRUE ) {
// todo (6.0) : this one may be ok because of how I implemented multi-valued param handling
// - the expansion is done per-execution based on the "static" SQM
// - Note from Christian: The call to domainParameterXref.clearExpansions() in ConcreteSqmSelectQueryPlan is a concurrency issue when cached
// - This could be solved by using a method-local clone of domainParameterXref when multi-valued params exist
return false;
}
return true;
// for now at least, skip caching Criteria-based plans
// - especially wrt parameters atm; this works with HQL because the
// parameters are part of the query string; with Criteria, they're not.
return ! CRITERIA_HQL_STRING.equals( keySource.getQueryString() )
// At the moment we cannot cache query plan if there is filter enabled.
&& ! keySource.getLoadQueryInfluencers().hasEnabledFilters()
// At the moment we cannot cache query plan if it has an entity graph
&& keySource.getQueryOptions().getAppliedGraph().getSemantic() == null
// todo (6.0) : this one may be ok because of how I implemented multi-valued param handling
// - the expansion is done per-execution based on the "static" SQM
// - Note from Christian: The call to domainParameterXref.clearExpansions()
// in ConcreteSqmSelectQueryPlan is a concurrency issue when cached
// - This could be solved by using a method-local clone of domainParameterXref
// when multi-valued params exist
&& ! keySource.hasMultiValuedParameterBindingsChecker().get() == TRUE;
}
public static QueryInterpretationCache.Key generateNonSelectKey(InterpretationsKeySource keyDetails) {
@ -86,24 +79,26 @@ public class SqmInterpretationsKey implements QueryInterpretationCache.Key {
return null;
}
private final String query;
private final Class<?> resultType;
private final LockOptions lockOptions;
private final TupleTransformer<?> tupleTransformer;
private final ResultListTransformer resultListTransformer;
private final ResultListTransformer<?> resultListTransformer;
private final Collection<String> enabledFetchProfiles;
private SqmInterpretationsKey(
String query,
Class<?> resultType,
LockOptions lockOptions,
TupleTransformer<?> tupleTransformer,
ResultListTransformer resultListTransformer) {
ResultListTransformer<?> resultListTransformer,
Collection<String> enabledFetchProfiles) {
this.query = query;
this.resultType = resultType;
this.lockOptions = lockOptions;
this.tupleTransformer = tupleTransformer;
this.resultListTransformer = resultListTransformer;
this.enabledFetchProfiles = enabledFetchProfiles;
}
@Override
@ -114,7 +109,8 @@ public class SqmInterpretationsKey implements QueryInterpretationCache.Key {
// Since lock options are mutable, we need a copy for the cache key
lockOptions.makeCopy(),
tupleTransformer,
resultListTransformer
resultListTransformer,
enabledFetchProfiles
);
}
@ -137,16 +133,12 @@ public class SqmInterpretationsKey implements QueryInterpretationCache.Key {
&& areEqual( resultType, that.resultType )
&& areEqual( lockOptions, that.lockOptions )
&& areEqual( tupleTransformer, that.tupleTransformer )
&& areEqual( resultListTransformer, that.resultListTransformer );
&& areEqual( resultListTransformer, that.resultListTransformer )
&& areEqual( enabledFetchProfiles, that.enabledFetchProfiles );
}
private <T> boolean areEqual(T o1, T o2) {
if ( o1 == null ) {
return o2 == null;
}
else {
return o1.equals( o2 );
}
return o1 == null ? o2 == null : o1.equals(o2);
}
@Override