HHH-16983 Avoid unnecessary allocations for HQL interpretation caching

This commit is contained in:
Christian Beikov 2023-07-24 12:55:40 +02:00
parent b9e5d3a6bc
commit 90eb697020
3 changed files with 67 additions and 45 deletions

View File

@ -744,7 +744,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
.resolveHqlInterpretation( .resolveHqlInterpretation(
hql, hql,
resultType, resultType,
s -> queryEngine.getHqlTranslator().translate( hql, resultType ) queryEngine.getHqlTranslator()
); );
} }

View File

@ -12,6 +12,7 @@ import java.util.function.Supplier;
import org.hibernate.internal.util.collections.BoundedConcurrentHashMap; import org.hibernate.internal.util.collections.BoundedConcurrentHashMap;
import org.hibernate.query.QueryLogging; import org.hibernate.query.QueryLogging;
import org.hibernate.query.hql.HqlTranslator;
import org.hibernate.query.spi.HqlInterpretation; import org.hibernate.query.spi.HqlInterpretation;
import org.hibernate.query.spi.NonSelectQueryPlan; import org.hibernate.query.spi.NonSelectQueryPlan;
import org.hibernate.query.spi.ParameterMetadataImplementor; import org.hibernate.query.spi.ParameterMetadataImplementor;
@ -40,7 +41,7 @@ public class QueryInterpretationCacheStandardImpl implements QueryInterpretation
*/ */
private final BoundedConcurrentHashMap<Key, QueryPlan> queryPlanCache; private final BoundedConcurrentHashMap<Key, QueryPlan> queryPlanCache;
private final BoundedConcurrentHashMap<String, HqlInterpretation> hqlInterpretationCache; private final BoundedConcurrentHashMap<Object, HqlInterpretation> hqlInterpretationCache;
private final BoundedConcurrentHashMap<String, ParameterInterpretation> nativeQueryParamCache; private final BoundedConcurrentHashMap<String, ParameterInterpretation> nativeQueryParamCache;
private final Supplier<StatisticsImplementor> statisticsSupplier; private final Supplier<StatisticsImplementor> statisticsSupplier;
@ -99,53 +100,35 @@ public class QueryInterpretationCacheStandardImpl implements QueryInterpretation
log.tracef( "QueryPlan#cacheNonSelectQueryPlan(%s)", key ); log.tracef( "QueryPlan#cacheNonSelectQueryPlan(%s)", key );
} }
private static String typedKey(String queryString, Class<?> expectedResultType) { @Override
return expectedResultType.getName() + ":" + queryString; public HqlInterpretation resolveHqlInterpretation(
} String queryString,
Class<?> expectedResultType,
private void putInCache(String queryString, Class<?> expectedResultType, HqlInterpretation hqlInterpretation) { Function<String, SqmStatement<?>> creator) {
final SqmStatement<?> statement = hqlInterpretation.getSqmStatement(); return resolveHqlInterpretation( queryString, expectedResultType, new HqlTranslator() {
if ( statement instanceof SqmSelectStatement && expectedResultType != null ) { @Override
hqlInterpretationCache.put( typedKey( queryString, expectedResultType ), hqlInterpretation); public <R> SqmStatement<R> translate(String hql, Class<R> expectedResultType) {
} //noinspection unchecked
else { return (SqmStatement<R>) creator.apply( hql );
hqlInterpretationCache.put( queryString, hqlInterpretation );
}
}
private HqlInterpretation getFromCache(String queryString, Class<?> expectedResultType) {
if ( expectedResultType != null ) {
final HqlInterpretation typedHqlInterpretation =
hqlInterpretationCache.get( typedKey( queryString, expectedResultType ) );
if ( typedHqlInterpretation != null ) {
return typedHqlInterpretation;
} }
} } );
final HqlInterpretation hqlInterpretation = hqlInterpretationCache.get( queryString );
if ( hqlInterpretation != null ) {
final SqmStatement<?> statement = hqlInterpretation.getSqmStatement();
if ( statement instanceof SqmSelectStatement && expectedResultType != null ) {
final Class<?> resultType = ((SqmSelectStatement<?>) statement).getSelection().getJavaType();
return expectedResultType.equals( resultType ) ? hqlInterpretation : null;
}
else {
return hqlInterpretation;
}
}
else {
return null;
}
} }
@Override @Override
public HqlInterpretation resolveHqlInterpretation( public HqlInterpretation resolveHqlInterpretation(
String queryString, String queryString,
Class<?> expectedResultType, Class<?> expectedResultType,
Function<String, SqmStatement<?>> creator) { HqlTranslator translator) {
log.tracef( "QueryPlan#resolveHqlInterpretation( `%s` )", queryString ); log.tracef( "QueryPlan#resolveHqlInterpretation( `%s` )", queryString );
final HqlInterpretation existing = getFromCache( queryString, expectedResultType ); final Object cacheKey;
if ( expectedResultType != null ) {
cacheKey = new HqlInterpretationCacheKey( queryString, expectedResultType );
}
else {
cacheKey = queryString;
}
final HqlInterpretation existing = hqlInterpretationCache.get( cacheKey );
if ( existing != null ) { if ( existing != null ) {
final StatisticsImplementor statistics = statisticsSupplier.get(); final StatisticsImplementor statistics = statisticsSupplier.get();
if ( statistics.isStatisticsEnabled() ) { if ( statistics.isStatisticsEnabled() ) {
@ -154,22 +137,27 @@ public class QueryInterpretationCacheStandardImpl implements QueryInterpretation
return existing; return existing;
} }
else { else {
final HqlInterpretation hqlInterpretation = final HqlInterpretation hqlInterpretation = createHqlInterpretation(
createHqlInterpretation( queryString, creator, statisticsSupplier ); queryString,
putInCache( queryString, expectedResultType, hqlInterpretation ); expectedResultType,
translator,
statisticsSupplier
);
hqlInterpretationCache.put( cacheKey, hqlInterpretation );
return hqlInterpretation; return hqlInterpretation;
} }
} }
protected static HqlInterpretation createHqlInterpretation( protected static HqlInterpretation createHqlInterpretation(
String queryString, String queryString,
Function<String, SqmStatement<?>> creator, Class<?> expectedResultType,
HqlTranslator translator,
Supplier<StatisticsImplementor> statisticsSupplier) { Supplier<StatisticsImplementor> statisticsSupplier) {
final StatisticsImplementor statistics = statisticsSupplier.get(); final StatisticsImplementor statistics = statisticsSupplier.get();
final boolean stats = statistics.isStatisticsEnabled(); final boolean stats = statistics.isStatisticsEnabled();
final long startTime = stats ? System.nanoTime() : 0L; final long startTime = stats ? System.nanoTime() : 0L;
final SqmStatement<?> sqmStatement = creator.apply( queryString ); final SqmStatement<?> sqmStatement = translator.translate( queryString, expectedResultType );
final ParameterMetadataImplementor parameterMetadata; final ParameterMetadataImplementor parameterMetadata;
final DomainParameterXref domainParameterXref; final DomainParameterXref domainParameterXref;
@ -219,4 +207,32 @@ public class QueryInterpretationCacheStandardImpl implements QueryInterpretation
queryPlanCache.clear(); queryPlanCache.clear();
} }
private static final class HqlInterpretationCacheKey {
private final String queryString;
private final Class<?> expectedResultType;
public HqlInterpretationCacheKey(String queryString, Class<?> expectedResultType) {
this.queryString = queryString;
this.expectedResultType = expectedResultType;
}
@Override
public boolean equals(Object o) {
if ( o.getClass() != HqlInterpretationCacheKey.class ) {
return false;
}
final HqlInterpretationCacheKey that = (HqlInterpretationCacheKey) o;
return queryString.equals( that.queryString )
&& expectedResultType.equals( that.expectedResultType );
}
@Override
public int hashCode() {
int result = queryString.hashCode();
result = 31 * result + expectedResultType.hashCode();
return result;
}
}
} }

View File

@ -10,6 +10,7 @@ import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.hibernate.Incubating; import org.hibernate.Incubating;
import org.hibernate.query.hql.HqlTranslator;
import org.hibernate.query.sql.spi.ParameterInterpretation; import org.hibernate.query.sql.spi.ParameterInterpretation;
import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.SqmStatement;
@ -36,8 +37,13 @@ public interface QueryInterpretationCache {
int getNumberOfCachedHqlInterpretations(); int getNumberOfCachedHqlInterpretations();
int getNumberOfCachedQueryPlans(); int getNumberOfCachedQueryPlans();
@Deprecated(forRemoval = true)
HqlInterpretation resolveHqlInterpretation(String queryString, Class<?> expectedResultType, Function<String, SqmStatement<?>> creator); HqlInterpretation resolveHqlInterpretation(String queryString, Class<?> expectedResultType, Function<String, SqmStatement<?>> creator);
default HqlInterpretation resolveHqlInterpretation(String queryString, Class<?> expectedResultType, HqlTranslator translator) {
return resolveHqlInterpretation( queryString, expectedResultType, s -> translator.translate( queryString, expectedResultType ) );
}
<R> SelectQueryPlan<R> resolveSelectQueryPlan(Key key, Supplier<SelectQueryPlan<R>> creator); <R> SelectQueryPlan<R> resolveSelectQueryPlan(Key key, Supplier<SelectQueryPlan<R>> creator);
NonSelectQueryPlan getNonSelectQueryPlan(Key key); NonSelectQueryPlan getNonSelectQueryPlan(Key key);