HHH-16815 Improvements in SqmInterpretationsKey

This commit is contained in:
Sanne Grinovero 2023-06-27 20:47:00 +01:00 committed by Sanne Grinovero
parent 4095e16212
commit dfa26e0b5c
2 changed files with 62 additions and 22 deletions

View File

@ -512,7 +512,7 @@ public class LockOptions implements Serializable {
} }
/** /**
* Make a copy. * Make a copy. The new copy will be mutable even if the original wasn't.
* *
* @return The copy * @return The copy
*/ */
@ -522,6 +522,22 @@ public class LockOptions implements Serializable {
return copy; return copy;
} }
/**
* Make a copy, unless this is an immutable instance.
*
* @return The copy, or this if it was immutable.
*/
public LockOptions makeDefensiveCopy() {
if ( immutable ) {
return this;
}
else {
final LockOptions copy = new LockOptions();
copy( this, copy );
return copy;
}
}
/** /**
* Copy the given lock options into this instance, * Copy the given lock options into this instance,
* merging the alias-specific lock modes. * merging the alias-specific lock modes.

View File

@ -7,8 +7,10 @@
package org.hibernate.query.sqm.internal; package org.hibernate.query.sqm.internal;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Supplier; import java.util.function.Supplier;
import jakarta.persistence.criteria.Order; import jakarta.persistence.criteria.Order;
@ -26,7 +28,7 @@ import static org.hibernate.query.spi.AbstractSelectionQuery.CRITERIA_HQL_STRING
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class SqmInterpretationsKey implements QueryInterpretationCache.Key { public final class SqmInterpretationsKey implements QueryInterpretationCache.Key {
public interface CacheabilityInfluencers { public interface CacheabilityInfluencers {
boolean isQueryPlanCacheable(); boolean isQueryPlanCacheable();
String getQueryString(); String getQueryString();
@ -43,16 +45,18 @@ public class SqmInterpretationsKey implements QueryInterpretationCache.Key {
public static SqmInterpretationsKey createInterpretationsKey(InterpretationsKeySource keySource) { public static SqmInterpretationsKey createInterpretationsKey(InterpretationsKeySource keySource) {
if ( isCacheable ( keySource ) ) { if ( isCacheable ( keySource ) ) {
return new SqmInterpretationsKey( final Object query = CRITERIA_HQL_STRING.equals( keySource.getQueryString() )
CRITERIA_HQL_STRING.equals( keySource.getQueryString() )
? keySource.getSqmStatement() ? keySource.getSqmStatement()
: keySource.getQueryString(), : keySource.getQueryString();
return new SqmInterpretationsKey(
query,
query.hashCode(),
keySource.getResultType(), keySource.getResultType(),
keySource.getOrder(), keySource.getOrder(),
keySource.getQueryOptions().getLockOptions(), keySource.getQueryOptions().getLockOptions(),
keySource.getQueryOptions().getTupleTransformer(), keySource.getQueryOptions().getTupleTransformer(),
keySource.getQueryOptions().getResultListTransformer(), keySource.getQueryOptions().getResultListTransformer(),
new HashSet<>( keySource.getLoadQueryInfluencers().getEnabledFetchProfileNames() ) memoryEfficientDefensiveSetCopy( keySource.getLoadQueryInfluencers().getEnabledFetchProfileNames() )
); );
} }
else { else {
@ -60,6 +64,25 @@ public class SqmInterpretationsKey implements QueryInterpretationCache.Key {
} }
} }
private static Collection<String> memoryEfficientDefensiveSetCopy(final Set<String> set) {
if ( set == null ) {
return null;
}
else {
switch ( set.size() ) {
case 0:
return null;
case 1:
return Set.of( set.iterator().next() );
case 2:
final Iterator<String> iterator = set.iterator();
return Set.of( iterator.next(), iterator.next() );
default:
return Set.copyOf( set );
}
}
}
private static boolean isCacheable(InterpretationsKeySource keySource) { private static boolean isCacheable(InterpretationsKeySource keySource) {
assert keySource.getQueryOptions().getAppliedGraph() != null; assert keySource.getQueryOptions().getAppliedGraph() != null;
@ -95,9 +118,11 @@ public class SqmInterpretationsKey implements QueryInterpretationCache.Key {
private final TupleTransformer<?> tupleTransformer; private final TupleTransformer<?> tupleTransformer;
private final ResultListTransformer<?> resultListTransformer; private final ResultListTransformer<?> resultListTransformer;
private final Collection<String> enabledFetchProfiles; private final Collection<String> enabledFetchProfiles;
private final int hashcode;
private SqmInterpretationsKey( private SqmInterpretationsKey(
Object query, Object query,
int hash,
Class<?> resultType, Class<?> resultType,
List<Order> order, List<Order> order,
LockOptions lockOptions, LockOptions lockOptions,
@ -105,6 +130,7 @@ public class SqmInterpretationsKey implements QueryInterpretationCache.Key {
ResultListTransformer<?> resultListTransformer, ResultListTransformer<?> resultListTransformer,
Collection<String> enabledFetchProfiles) { Collection<String> enabledFetchProfiles) {
this.query = query; this.query = query;
this.hashcode = hash;
this.resultType = resultType; this.resultType = resultType;
this.order = order; this.order = order;
this.lockOptions = lockOptions; this.lockOptions = lockOptions;
@ -117,10 +143,11 @@ public class SqmInterpretationsKey implements QueryInterpretationCache.Key {
public QueryInterpretationCache.Key prepareForStore() { public QueryInterpretationCache.Key prepareForStore() {
return new SqmInterpretationsKey( return new SqmInterpretationsKey(
query, query,
hashcode,
resultType, resultType,
// Since lock options are mutable, we need a copy for the cache key
order, order,
lockOptions.makeCopy(), // Since lock options might be mutable, we need a copy for the cache key
lockOptions.makeDefensiveCopy(),
tupleTransformer, tupleTransformer,
resultListTransformer, resultListTransformer,
enabledFetchProfiles enabledFetchProfiles
@ -137,26 +164,23 @@ public class SqmInterpretationsKey implements QueryInterpretationCache.Key {
if ( this == o ) { if ( this == o ) {
return true; return true;
} }
if ( o == null || getClass() != o.getClass() ) { if ( o == null || SqmInterpretationsKey.class != o.getClass() ) {
return false; return false;
} }
final SqmInterpretationsKey that = (SqmInterpretationsKey) o; final SqmInterpretationsKey that = (SqmInterpretationsKey) o;
return query.equals( that.query ) return this.hashcode == o.hashCode() //check this first as some other checks are expensive
&& areEqual( resultType, that.resultType ) && query.equals( that.query )
&& areEqual( order, that.order ) && Objects.equals( resultType, that.resultType )
&& areEqual( lockOptions, that.lockOptions ) && Objects.equals( order, that.order )
&& areEqual( tupleTransformer, that.tupleTransformer ) && Objects.equals( lockOptions, that.lockOptions )
&& areEqual( resultListTransformer, that.resultListTransformer ) && Objects.equals( tupleTransformer, that.tupleTransformer )
&& areEqual( enabledFetchProfiles, that.enabledFetchProfiles ); && Objects.equals( resultListTransformer, that.resultListTransformer )
} && Objects.equals( enabledFetchProfiles, that.enabledFetchProfiles );
private <T> boolean areEqual(T o1, T o2) {
return o1 == null ? o2 == null : o1.equals(o2);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return query.hashCode(); return hashcode;
} }
} }