Skip to content

Commit 3cd50a0

Browse files
committed
HHH-17002 Query plan caching for CriteriaQuery based on query structure
1 parent f8f1f3e commit 3cd50a0

File tree

132 files changed

+730
-409
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

132 files changed

+730
-409
lines changed

hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java

+7
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
230230
private TimeZone jdbcTimeZone;
231231
private final ValueHandlingMode criteriaValueHandlingMode;
232232
private final boolean criteriaCopyTreeEnabled;
233+
private final boolean criteriaPlanCacheEnabled;
233234
private final boolean nativeJdbcParametersIgnored;
234235
private final ImmutableEntityUpdateQueryHandlingMode immutableEntityUpdateQueryHandlingMode;
235236
// These two settings cannot be modified from the builder,
@@ -488,6 +489,7 @@ public SessionFactoryOptionsBuilder(StandardServiceRegistry serviceRegistry, Boo
488489

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

492494
nativeJdbcParametersIgnored =
493495
getBoolean( AvailableSettings.NATIVE_IGNORE_JDBC_PARAMETERS, settings, false );
@@ -1126,6 +1128,11 @@ public boolean isCriteriaCopyTreeEnabled() {
11261128
return criteriaCopyTreeEnabled;
11271129
}
11281130

1131+
@Override
1132+
public boolean isCriteriaPlanCacheEnabled() {
1133+
return criteriaPlanCacheEnabled;
1134+
}
1135+
11291136
@Override
11301137
public boolean getNativeJdbcParametersIgnored() {
11311138
return nativeJdbcParametersIgnored;

hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java

+5
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,11 @@ public boolean isCriteriaCopyTreeEnabled() {
394394
return delegate.isCriteriaCopyTreeEnabled();
395395
}
396396

397+
@Override
398+
public boolean isCriteriaPlanCacheEnabled() {
399+
return delegate.isCriteriaPlanCacheEnabled();
400+
}
401+
397402
public boolean getNativeJdbcParametersIgnored() {
398403
return delegate.getNativeJdbcParametersIgnored();
399404
}

hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java

+6
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,12 @@ default ValueHandlingMode getCriteriaValueHandlingMode() {
393393
default boolean isCriteriaCopyTreeEnabled() {
394394
return false;
395395
}
396+
/**
397+
* @see org.hibernate.cfg.AvailableSettings#CRITERIA_PLAN_CACHE_ENABLED
398+
*/
399+
default boolean isCriteriaPlanCacheEnabled() {
400+
return false;
401+
}
396402

397403
/**
398404
* @see org.hibernate.cfg.AvailableSettings#NATIVE_IGNORE_JDBC_PARAMETERS

hibernate-core/src/main/java/org/hibernate/cfg/QuerySettings.java

+15
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,21 @@ public interface QuerySettings {
156156
*/
157157
String CRITERIA_COPY_TREE = "hibernate.criteria.copy_tree";
158158

159+
/**
160+
* When enabled, specifies that {@linkplain org.hibernate.query.Query queries}
161+
* created via {@link jakarta.persistence.EntityManager#createQuery(CriteriaQuery)},
162+
* {@link jakarta.persistence.EntityManager#createQuery(CriteriaUpdate)} or
163+
* {@link jakarta.persistence.EntityManager#createQuery(CriteriaDelete)} cache
164+
* their interpretations in the query plan cache.
165+
* <p>
166+
* If disabled, queries are interpreted on first access without caching.
167+
*
168+
* @settingDefault {@code false} (disabled) - criteria queries do not use query plan caching.
169+
*
170+
* @since 7.0
171+
*/
172+
String CRITERIA_PLAN_CACHE_ENABLED = "hibernate.criteria.plan_cache_enabled";
173+
159174
/**
160175
* When enabled, ordinal parameters (represented by the {@code ?} placeholder) in
161176
* native queries will be ignored.

hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java

+10
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,16 @@ public boolean isCriteriaCopyTreeEnabled() {
248248
return delegate.isCriteriaCopyTreeEnabled();
249249
}
250250

251+
@Override
252+
public boolean isCriteriaPlanCacheEnabled() {
253+
return delegate.isCriteriaPlanCacheEnabled();
254+
}
255+
256+
@Override
257+
public void setCriteriaPlanCacheEnabled(boolean jpaCriteriaCacheEnabled) {
258+
delegate.setCriteriaPlanCacheEnabled( jpaCriteriaCacheEnabled );
259+
}
260+
251261
@Override
252262
public boolean getNativeJdbcParametersIgnored() {
253263
return delegate.getNativeJdbcParametersIgnored();

hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionContractImplementor.java

+4
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,10 @@ default String bestGuessEntityName(Object object, EntityEntry entry) {
381381

382382
boolean isCriteriaCopyTreeEnabled();
383383

384+
void setCriteriaPlanCacheEnabled(boolean jpaCriteriaCacheEnabled);
385+
386+
boolean isCriteriaPlanCacheEnabled();
387+
384388
boolean getNativeJdbcParametersIgnored();
385389

386390
void setNativeJdbcParametersIgnored(boolean nativeJdbcParametersIgnored);

hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java

+10
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,16 @@ public boolean isCriteriaCopyTreeEnabled() {
498498
return delegate.isCriteriaCopyTreeEnabled();
499499
}
500500

501+
@Override
502+
public boolean isCriteriaPlanCacheEnabled() {
503+
return delegate.isCriteriaPlanCacheEnabled();
504+
}
505+
506+
@Override
507+
public void setCriteriaPlanCacheEnabled(boolean jpaCriteriaCacheEnabled) {
508+
delegate.setCriteriaPlanCacheEnabled( jpaCriteriaCacheEnabled );
509+
}
510+
501511
@Override
502512
public boolean getNativeJdbcParametersIgnored() {
503513
return delegate.getNativeJdbcParametersIgnored();

hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java

+12
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
167167
private Integer jdbcBatchSize;
168168

169169
private boolean criteriaCopyTreeEnabled;
170+
private boolean criteriaPlanCacheEnabled;
170171

171172
private boolean nativeJdbcParametersIgnored;
172173

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

196197
setCriteriaCopyTreeEnabled( factoryOptions.isCriteriaCopyTreeEnabled() );
198+
setCriteriaPlanCacheEnabled( factoryOptions.isCriteriaPlanCacheEnabled() );
197199
setNativeJdbcParametersIgnored( factoryOptions.getNativeJdbcParametersIgnored() );
198200
setCacheMode( factoryOptions.getInitialSessionCacheMode() );
199201

@@ -803,6 +805,16 @@ public boolean isCriteriaCopyTreeEnabled() {
803805
return criteriaCopyTreeEnabled;
804806
}
805807

808+
@Override
809+
public boolean isCriteriaPlanCacheEnabled() {
810+
return criteriaPlanCacheEnabled;
811+
}
812+
813+
@Override
814+
public void setCriteriaPlanCacheEnabled(boolean criteriaPlanCacheEnabled) {
815+
this.criteriaPlanCacheEnabled = criteriaPlanCacheEnabled;
816+
}
817+
806818
@Override
807819
public boolean getNativeJdbcParametersIgnored() {
808820
return nativeJdbcParametersIgnored;

hibernate-core/src/main/java/org/hibernate/query/hql/internal/FullyQualifiedReflectivePathTerminal.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.hibernate.query.sqm.SemanticQueryWalker;
2222
import org.hibernate.query.sqm.spi.SqmCreationContext;
2323
import org.hibernate.query.sqm.tree.SqmCopyContext;
24+
import org.hibernate.query.sqm.tree.SqmRenderContext;
2425
import org.hibernate.query.sqm.tree.expression.SqmEnumLiteral;
2526
import org.hibernate.query.sqm.tree.expression.SqmExpression;
2627
import org.hibernate.query.sqm.tree.expression.SqmFieldLiteral;
@@ -156,7 +157,7 @@ public void applyInferableType(@Nullable SqmExpressible<?> type) {
156157
}
157158

158159
@Override
159-
public void appendHqlString(StringBuilder hql) {
160+
public void appendHqlString(StringBuilder hql, SqmRenderContext context) {
160161
hql.append( getParent().getFullPath() );
161162
hql.append( '.' );
162163
hql.append( getLocalName() );

hibernate-core/src/main/java/org/hibernate/query/sqm/DiscriminatorSqmPath.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import org.hibernate.metamodel.model.domain.EntityDomainType;
99
import org.hibernate.query.PathException;
1010
import org.hibernate.query.hql.spi.SqmCreationState;
11+
import org.hibernate.query.sqm.tree.SqmRenderContext;
1112
import org.hibernate.query.sqm.tree.domain.SqmPath;
1213
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
1314

@@ -18,9 +19,9 @@
1819
*/
1920
public interface DiscriminatorSqmPath<T> extends SqmPath<T> {
2021
@Override
21-
default void appendHqlString(StringBuilder hql) {
22+
default void appendHqlString(StringBuilder hql, SqmRenderContext context) {
2223
hql.append( "type(" );
23-
getLhs().appendHqlString( hql );
24+
getLhs().appendHqlString( hql, context );
2425
hql.append( ')' );
2526
}
2627

hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmAggregateFunction.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver;
1414
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
1515
import org.hibernate.query.sqm.tree.SqmCopyContext;
16+
import org.hibernate.query.sqm.tree.SqmRenderContext;
1617
import org.hibernate.query.sqm.tree.SqmTypedNode;
1718
import org.hibernate.query.sqm.tree.expression.SqmAggregateFunction;
1819
import org.hibernate.query.sqm.tree.expression.SqmDistinct;
@@ -97,28 +98,28 @@ public SqmPredicate getFilter() {
9798
}
9899

99100
@Override
100-
public void appendHqlString(StringBuilder hql) {
101+
public void appendHqlString(StringBuilder hql, SqmRenderContext context) {
101102
final List<? extends SqmTypedNode<?>> arguments = getArguments();
102103
hql.append( getFunctionName() );
103104
hql.append( '(' );
104105
int i = 1;
105106
if ( arguments.get( 0 ) instanceof SqmDistinct<?> ) {
106-
arguments.get( 0 ).appendHqlString( hql );
107+
arguments.get( 0 ).appendHqlString( hql, context );
107108
if ( arguments.size() > 1 ) {
108109
hql.append( ' ' );
109-
arguments.get( 1 ).appendHqlString( hql );
110+
arguments.get( 1 ).appendHqlString( hql, context );
110111
i = 2;
111112
}
112113
}
113114
for ( ; i < arguments.size(); i++ ) {
114115
hql.append(", ");
115-
arguments.get( i ).appendHqlString( hql );
116+
arguments.get( i ).appendHqlString( hql, context );
116117
}
117118

118119
hql.append( ')' );
119120
if ( filter != null ) {
120121
hql.append( " filter (where " );
121-
filter.appendHqlString( hql );
122+
filter.appendHqlString( hql, context );
122123
hql.append( ')' );
123124
}
124125
}

hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmOrderedSetAggregateFunction.java

+8-7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver;
1515
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
1616
import org.hibernate.query.sqm.tree.SqmCopyContext;
17+
import org.hibernate.query.sqm.tree.SqmRenderContext;
1718
import org.hibernate.query.sqm.tree.SqmTypedNode;
1819
import org.hibernate.query.sqm.tree.expression.SqmDistinct;
1920
import org.hibernate.query.sqm.tree.expression.SqmOrderedSetAggregateFunction;
@@ -133,39 +134,39 @@ public SqmOrderByClause getWithinGroup() {
133134
}
134135

135136
@Override
136-
public void appendHqlString(StringBuilder hql) {
137+
public void appendHqlString(StringBuilder hql, SqmRenderContext context) {
137138
final List<? extends SqmTypedNode<?>> arguments = getArguments();
138139
hql.append( getFunctionName() );
139140
hql.append( '(' );
140141
int i = 1;
141142
if ( arguments.get( 0 ) instanceof SqmDistinct<?> ) {
142-
arguments.get( 0 ).appendHqlString( hql );
143+
arguments.get( 0 ).appendHqlString( hql, context );
143144
if ( arguments.size() > 1 ) {
144145
hql.append( ' ' );
145-
arguments.get( 1 ).appendHqlString( hql );
146+
arguments.get( 1 ).appendHqlString( hql, context );
146147
i = 2;
147148
}
148149
}
149150
for ( ; i < arguments.size(); i++ ) {
150151
hql.append(", ");
151-
arguments.get( i ).appendHqlString( hql );
152+
arguments.get( i ).appendHqlString( hql, context );
152153
}
153154

154155
hql.append( ')' );
155156
if ( withinGroup != null ) {
156157
hql.append( " within group (order by " );
157158
final List<SqmSortSpecification> sortSpecifications = withinGroup.getSortSpecifications();
158-
sortSpecifications.get( 0 ).appendHqlString( hql );
159+
sortSpecifications.get( 0 ).appendHqlString( hql, context );
159160
for ( int j = 1; j < sortSpecifications.size(); j++ ) {
160161
hql.append( ", " );
161-
sortSpecifications.get( j ).appendHqlString( hql );
162+
sortSpecifications.get( j ).appendHqlString( hql, context );
162163
}
163164
hql.append( ')' );
164165
}
165166

166167
if ( getFilter() != null ) {
167168
hql.append( " filter (where " );
168-
getFilter().appendHqlString( hql );
169+
getFilter().appendHqlString( hql, context );
169170
hql.append( ')' );
170171
}
171172
}

hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingSqmWindowFunction.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.hibernate.query.sqm.produce.function.FunctionReturnTypeResolver;
1414
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
1515
import org.hibernate.query.sqm.tree.SqmCopyContext;
16+
import org.hibernate.query.sqm.tree.SqmRenderContext;
1617
import org.hibernate.query.sqm.tree.SqmTypedNode;
1718
import org.hibernate.query.sqm.tree.expression.SqmDistinct;
1819
import org.hibernate.query.sqm.tree.expression.SqmWindowFunction;
@@ -115,22 +116,22 @@ public Boolean getFromFirst() {
115116
}
116117

117118
@Override
118-
public void appendHqlString(StringBuilder hql) {
119+
public void appendHqlString(StringBuilder hql, SqmRenderContext context) {
119120
final List<? extends SqmTypedNode<?>> arguments = getArguments();
120121
hql.append( getFunctionName() );
121122
hql.append( '(' );
122123
int i = 1;
123124
if ( arguments.get( 0 ) instanceof SqmDistinct<?> ) {
124-
arguments.get( 0 ).appendHqlString( hql );
125+
arguments.get( 0 ).appendHqlString( hql, context );
125126
if ( arguments.size() > 1 ) {
126127
hql.append( ' ' );
127-
arguments.get( 1 ).appendHqlString( hql );
128+
arguments.get( 1 ).appendHqlString( hql, context );
128129
i = 2;
129130
}
130131
}
131132
for ( ; i < arguments.size(); i++ ) {
132133
hql.append(", ");
133-
arguments.get( i ).appendHqlString( hql );
134+
arguments.get( i ).appendHqlString( hql, context );
134135
}
135136

136137
hql.append( ')' );
@@ -152,7 +153,7 @@ public void appendHqlString(StringBuilder hql) {
152153
}
153154
if ( filter != null ) {
154155
hql.append( " filter (where " );
155-
filter.appendHqlString( hql );
156+
filter.appendHqlString( hql, context );
156157
hql.append( ')' );
157158
}
158159
}

hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java

+20
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ public class QuerySqmImpl<R>
121121
implements SqmQueryImplementor<R>, InterpretationsKeySource, DomainQueryExecutionContext {
122122

123123
private final String hql;
124+
private final Object queryStringCacheKey;
124125
private SqmStatement<R> sqm;
125126

126127
private final ParameterMetadataImplementor parameterMetadata;
@@ -170,6 +171,7 @@ public QuerySqmImpl(
170171
SharedSessionContractImplementor session) {
171172
super( session );
172173
this.hql = hql;
174+
this.queryStringCacheKey = hql;
173175
this.resultType = resultType;
174176

175177
sqm = hqlInterpretation.getSqmStatement();
@@ -200,9 +202,22 @@ public QuerySqmImpl(
200202
hql = CRITERIA_HQL_STRING;
201203
if ( producer.isCriteriaCopyTreeEnabled() ) {
202204
sqm = criteria.copy( SqmCopyContext.simpleContext() );
205+
if ( producer.isCriteriaPlanCacheEnabled() ) {
206+
queryStringCacheKey = sqm.toHqlString();
207+
setQueryPlanCacheable( true );
208+
}
209+
else {
210+
queryStringCacheKey = sqm;
211+
}
203212
}
204213
else {
205214
sqm = criteria;
215+
if ( producer.isCriteriaPlanCacheEnabled() ) {
216+
queryStringCacheKey = sqm.toHqlString();
217+
}
218+
else {
219+
queryStringCacheKey = sqm;
220+
}
206221
// Cache immutable query plans by default
207222
setQueryPlanCacheable( true );
208223
}
@@ -261,6 +276,11 @@ public String getQueryString() {
261276
return hql;
262277
}
263278

279+
@Override
280+
public Object getQueryStringCacheKey() {
281+
return queryStringCacheKey;
282+
}
283+
264284
@Override
265285
public SqmStatement<R> getSqmStatement() {
266286
return sqm;

0 commit comments

Comments
 (0)