Skip to content

HHH-17002 Query plan caching for CriteriaQuery based on query structure #10005

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
private TimeZone jdbcTimeZone;
private final ValueHandlingMode criteriaValueHandlingMode;
private final boolean criteriaCopyTreeEnabled;
private final boolean criteriaPlanCacheEnabled;
private final boolean nativeJdbcParametersIgnored;
private final ImmutableEntityUpdateQueryHandlingMode immutableEntityUpdateQueryHandlingMode;
// These two settings cannot be modified from the builder,
Expand Down Expand Up @@ -488,6 +489,7 @@ public SessionFactoryOptionsBuilder(StandardServiceRegistry serviceRegistry, Boo

criteriaValueHandlingMode = ValueHandlingMode.interpret( settings.get( CRITERIA_VALUE_HANDLING_MODE ) );
criteriaCopyTreeEnabled = getBoolean( AvailableSettings.CRITERIA_COPY_TREE, settings, jpaBootstrap );
criteriaPlanCacheEnabled = getBoolean( AvailableSettings.CRITERIA_PLAN_CACHE_ENABLED, settings, false );

nativeJdbcParametersIgnored =
getBoolean( AvailableSettings.NATIVE_IGNORE_JDBC_PARAMETERS, settings, false );
Expand Down Expand Up @@ -1126,6 +1128,11 @@ public boolean isCriteriaCopyTreeEnabled() {
return criteriaCopyTreeEnabled;
}

@Override
public boolean isCriteriaPlanCacheEnabled() {
return criteriaPlanCacheEnabled;
}

@Override
public boolean getNativeJdbcParametersIgnored() {
return nativeJdbcParametersIgnored;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,11 @@ public boolean isCriteriaCopyTreeEnabled() {
return delegate.isCriteriaCopyTreeEnabled();
}

@Override
public boolean isCriteriaPlanCacheEnabled() {
return delegate.isCriteriaPlanCacheEnabled();
}

public boolean getNativeJdbcParametersIgnored() {
return delegate.getNativeJdbcParametersIgnored();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,12 @@ default ValueHandlingMode getCriteriaValueHandlingMode() {
default boolean isCriteriaCopyTreeEnabled() {
return false;
}
/**
* @see org.hibernate.cfg.AvailableSettings#CRITERIA_PLAN_CACHE_ENABLED
*/
default boolean isCriteriaPlanCacheEnabled() {
return false;
}

/**
* @see org.hibernate.cfg.AvailableSettings#NATIVE_IGNORE_JDBC_PARAMETERS
Expand Down
15 changes: 15 additions & 0 deletions hibernate-core/src/main/java/org/hibernate/cfg/QuerySettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,21 @@ public interface QuerySettings {
*/
String CRITERIA_COPY_TREE = "hibernate.criteria.copy_tree";

/**
* When enabled, specifies that {@linkplain org.hibernate.query.Query queries}
* created via {@link jakarta.persistence.EntityManager#createQuery(CriteriaQuery)},
* {@link jakarta.persistence.EntityManager#createQuery(CriteriaUpdate)} or
* {@link jakarta.persistence.EntityManager#createQuery(CriteriaDelete)} cache
* their interpretations in the query plan cache.
* <p>
* If disabled, queries are interpreted on first access without caching.
*
* @settingDefault {@code false} (disabled) - criteria queries do not use query plan caching.
*
* @since 7.0
*/
String CRITERIA_PLAN_CACHE_ENABLED = "hibernate.criteria.plan_cache_enabled";

/**
* When enabled, ordinal parameters (represented by the {@code ?} placeholder) in
* native queries will be ignored.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,16 @@ public boolean isCriteriaCopyTreeEnabled() {
return delegate.isCriteriaCopyTreeEnabled();
}

@Override
public boolean isCriteriaPlanCacheEnabled() {
return delegate.isCriteriaPlanCacheEnabled();
}

@Override
public void setCriteriaPlanCacheEnabled(boolean jpaCriteriaCacheEnabled) {
delegate.setCriteriaPlanCacheEnabled( jpaCriteriaCacheEnabled );
}

@Override
public boolean getNativeJdbcParametersIgnored() {
return delegate.getNativeJdbcParametersIgnored();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,10 @@ default String bestGuessEntityName(Object object, EntityEntry entry) {

boolean isCriteriaCopyTreeEnabled();

void setCriteriaPlanCacheEnabled(boolean jpaCriteriaCacheEnabled);

boolean isCriteriaPlanCacheEnabled();

boolean getNativeJdbcParametersIgnored();

void setNativeJdbcParametersIgnored(boolean nativeJdbcParametersIgnored);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,16 @@ public boolean isCriteriaCopyTreeEnabled() {
return delegate.isCriteriaCopyTreeEnabled();
}

@Override
public boolean isCriteriaPlanCacheEnabled() {
return delegate.isCriteriaPlanCacheEnabled();
}

@Override
public void setCriteriaPlanCacheEnabled(boolean jpaCriteriaCacheEnabled) {
delegate.setCriteriaPlanCacheEnabled( jpaCriteriaCacheEnabled );
}

@Override
public boolean getNativeJdbcParametersIgnored() {
return delegate.getNativeJdbcParametersIgnored();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
private Integer jdbcBatchSize;

private boolean criteriaCopyTreeEnabled;
private boolean criteriaPlanCacheEnabled;

private boolean nativeJdbcParametersIgnored;

Expand Down Expand Up @@ -194,6 +195,7 @@ public AbstractSharedSessionContract(SessionFactoryImpl factory, SessionCreation
entityNameResolver = new CoordinatingEntityNameResolver( factory, interceptor );

setCriteriaCopyTreeEnabled( factoryOptions.isCriteriaCopyTreeEnabled() );
setCriteriaPlanCacheEnabled( factoryOptions.isCriteriaPlanCacheEnabled() );
setNativeJdbcParametersIgnored( factoryOptions.getNativeJdbcParametersIgnored() );
setCacheMode( factoryOptions.getInitialSessionCacheMode() );

Expand Down Expand Up @@ -803,6 +805,16 @@ public boolean isCriteriaCopyTreeEnabled() {
return criteriaCopyTreeEnabled;
}

@Override
public boolean isCriteriaPlanCacheEnabled() {
return criteriaPlanCacheEnabled;
}

@Override
public void setCriteriaPlanCacheEnabled(boolean criteriaPlanCacheEnabled) {
this.criteriaPlanCacheEnabled = criteriaPlanCacheEnabled;
}

@Override
public boolean getNativeJdbcParametersIgnored() {
return nativeJdbcParametersIgnored;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.spi.SqmCreationContext;
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.SqmRenderContext;
import org.hibernate.query.sqm.tree.expression.SqmEnumLiteral;
import org.hibernate.query.sqm.tree.expression.SqmExpression;
import org.hibernate.query.sqm.tree.expression.SqmFieldLiteral;
Expand Down Expand Up @@ -156,7 +157,7 @@ public void applyInferableType(@Nullable SqmExpressible<?> type) {
}

@Override
public void appendHqlString(StringBuilder hql) {
public void appendHqlString(StringBuilder hql, SqmRenderContext context) {
hql.append( getParent().getFullPath() );
hql.append( '.' );
hql.append( getLocalName() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.PathException;
import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.tree.SqmRenderContext;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;

Expand All @@ -18,9 +19,9 @@
*/
public interface DiscriminatorSqmPath<T> extends SqmPath<T> {
@Override
default void appendHqlString(StringBuilder hql) {
default void appendHqlString(StringBuilder hql, SqmRenderContext context) {
hql.append( "type(" );
getLhs().appendHqlString( hql );
getLhs().appendHqlString( hql, context );
hql.append( ')' );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver;
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.SqmRenderContext;
import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.query.sqm.tree.expression.SqmAggregateFunction;
import org.hibernate.query.sqm.tree.expression.SqmDistinct;
Expand Down Expand Up @@ -97,28 +98,28 @@ public SqmPredicate getFilter() {
}

@Override
public void appendHqlString(StringBuilder hql) {
public void appendHqlString(StringBuilder hql, SqmRenderContext context) {
final List<? extends SqmTypedNode<?>> arguments = getArguments();
hql.append( getFunctionName() );
hql.append( '(' );
int i = 1;
if ( arguments.get( 0 ) instanceof SqmDistinct<?> ) {
arguments.get( 0 ).appendHqlString( hql );
arguments.get( 0 ).appendHqlString( hql, context );
if ( arguments.size() > 1 ) {
hql.append( ' ' );
arguments.get( 1 ).appendHqlString( hql );
arguments.get( 1 ).appendHqlString( hql, context );
i = 2;
}
}
for ( ; i < arguments.size(); i++ ) {
hql.append(", ");
arguments.get( i ).appendHqlString( hql );
arguments.get( i ).appendHqlString( hql, context );
}

hql.append( ')' );
if ( filter != null ) {
hql.append( " filter (where " );
filter.appendHqlString( hql );
filter.appendHqlString( hql, context );
hql.append( ')' );
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver;
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.SqmRenderContext;
import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.query.sqm.tree.expression.SqmDistinct;
import org.hibernate.query.sqm.tree.expression.SqmOrderedSetAggregateFunction;
Expand Down Expand Up @@ -133,39 +134,39 @@ public SqmOrderByClause getWithinGroup() {
}

@Override
public void appendHqlString(StringBuilder hql) {
public void appendHqlString(StringBuilder hql, SqmRenderContext context) {
final List<? extends SqmTypedNode<?>> arguments = getArguments();
hql.append( getFunctionName() );
hql.append( '(' );
int i = 1;
if ( arguments.get( 0 ) instanceof SqmDistinct<?> ) {
arguments.get( 0 ).appendHqlString( hql );
arguments.get( 0 ).appendHqlString( hql, context );
if ( arguments.size() > 1 ) {
hql.append( ' ' );
arguments.get( 1 ).appendHqlString( hql );
arguments.get( 1 ).appendHqlString( hql, context );
i = 2;
}
}
for ( ; i < arguments.size(); i++ ) {
hql.append(", ");
arguments.get( i ).appendHqlString( hql );
arguments.get( i ).appendHqlString( hql, context );
}

hql.append( ')' );
if ( withinGroup != null ) {
hql.append( " within group (order by " );
final List<SqmSortSpecification> sortSpecifications = withinGroup.getSortSpecifications();
sortSpecifications.get( 0 ).appendHqlString( hql );
sortSpecifications.get( 0 ).appendHqlString( hql, context );
for ( int j = 1; j < sortSpecifications.size(); j++ ) {
hql.append( ", " );
sortSpecifications.get( j ).appendHqlString( hql );
sortSpecifications.get( j ).appendHqlString( hql, context );
}
hql.append( ')' );
}

if ( getFilter() != null ) {
hql.append( " filter (where " );
getFilter().appendHqlString( hql );
getFilter().appendHqlString( hql, context );
hql.append( ')' );
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver;
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.query.sqm.tree.SqmCopyContext;
import org.hibernate.query.sqm.tree.SqmRenderContext;
import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.query.sqm.tree.expression.SqmDistinct;
import org.hibernate.query.sqm.tree.expression.SqmWindowFunction;
Expand Down Expand Up @@ -115,22 +116,22 @@ public Boolean getFromFirst() {
}

@Override
public void appendHqlString(StringBuilder hql) {
public void appendHqlString(StringBuilder hql, SqmRenderContext context) {
final List<? extends SqmTypedNode<?>> arguments = getArguments();
hql.append( getFunctionName() );
hql.append( '(' );
int i = 1;
if ( arguments.get( 0 ) instanceof SqmDistinct<?> ) {
arguments.get( 0 ).appendHqlString( hql );
arguments.get( 0 ).appendHqlString( hql, context );
if ( arguments.size() > 1 ) {
hql.append( ' ' );
arguments.get( 1 ).appendHqlString( hql );
arguments.get( 1 ).appendHqlString( hql, context );
i = 2;
}
}
for ( ; i < arguments.size(); i++ ) {
hql.append(", ");
arguments.get( i ).appendHqlString( hql );
arguments.get( i ).appendHqlString( hql, context );
}

hql.append( ')' );
Expand All @@ -152,7 +153,7 @@ public void appendHqlString(StringBuilder hql) {
}
if ( filter != null ) {
hql.append( " filter (where " );
filter.appendHqlString( hql );
filter.appendHqlString( hql, context );
hql.append( ')' );
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ public class QuerySqmImpl<R>
implements SqmQueryImplementor<R>, InterpretationsKeySource, DomainQueryExecutionContext {

private final String hql;
private final Object queryStringCacheKey;
private SqmStatement<R> sqm;

private final ParameterMetadataImplementor parameterMetadata;
Expand Down Expand Up @@ -170,6 +171,7 @@ public QuerySqmImpl(
SharedSessionContractImplementor session) {
super( session );
this.hql = hql;
this.queryStringCacheKey = hql;
this.resultType = resultType;

sqm = hqlInterpretation.getSqmStatement();
Expand Down Expand Up @@ -200,9 +202,22 @@ public QuerySqmImpl(
hql = CRITERIA_HQL_STRING;
if ( producer.isCriteriaCopyTreeEnabled() ) {
sqm = criteria.copy( SqmCopyContext.simpleContext() );
if ( producer.isCriteriaPlanCacheEnabled() ) {
queryStringCacheKey = sqm.toHqlString();
setQueryPlanCacheable( true );
}
else {
queryStringCacheKey = sqm;
}
}
else {
sqm = criteria;
if ( producer.isCriteriaPlanCacheEnabled() ) {
queryStringCacheKey = sqm.toHqlString();
}
else {
queryStringCacheKey = sqm;
}
// Cache immutable query plans by default
setQueryPlanCacheable( true );
}
Expand Down Expand Up @@ -261,6 +276,11 @@ public String getQueryString() {
return hql;
}

@Override
public Object getQueryStringCacheKey() {
return queryStringCacheKey;
}

@Override
public SqmStatement<R> getSqmStatement() {
return sqm;
Expand Down
Loading