Skip to content

Commit bd89d38

Browse files
gavinkingbeikov
authored andcommitted
HHH-17002 Reset query string cache key and parameter state when changing sqm statement
1 parent 3e8b28c commit bd89d38

File tree

8 files changed

+111
-68
lines changed

8 files changed

+111
-68
lines changed

hibernate-core/src/main/java/org/hibernate/query/range/CaseInsensitiveValue.java

+1-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
package org.hibernate.query.range;
66

77
import jakarta.persistence.criteria.CriteriaBuilder;
8-
import jakarta.persistence.criteria.Expression;
98
import jakarta.persistence.criteria.Path;
109
import jakarta.persistence.criteria.Predicate;
1110

@@ -24,14 +23,9 @@ record CaseInsensitiveValue(String value) implements Range<String> {
2423

2524
@Override
2625
public Predicate toPredicate(Path<? extends String> path, CriteriaBuilder builder) {
27-
// TODO: it would be much better to not do use literal,
28-
// and let it be treated as a parameter, but we
29-
// we run into the usual bug with parameters in
30-
// manipulated SQM trees
3126
@SuppressWarnings("unchecked")
3227
final Path<String> stringPath = (Path<String>) path; // safe, because String is final
33-
final Expression<String> literal = builder.literal( value.toLowerCase( Locale.ROOT ) );
34-
return builder.lower( stringPath ).equalTo( literal );
28+
return builder.lower( stringPath ).equalTo( value.toLowerCase( Locale.ROOT ) );
3529
}
3630

3731
@Override

hibernate-core/src/main/java/org/hibernate/query/range/LowerBound.java

+2-8
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
package org.hibernate.query.range;
66

77
import jakarta.persistence.criteria.CriteriaBuilder;
8-
import jakarta.persistence.criteria.Expression;
98
import jakarta.persistence.criteria.Path;
109
import jakarta.persistence.criteria.Predicate;
1110
import org.hibernate.internal.util.ReflectHelper;
@@ -24,14 +23,9 @@ record LowerBound<U extends Comparable<U>>(U bound, boolean open) implements Ran
2423

2524
@Override
2625
public Predicate toPredicate(Path<? extends U> path, CriteriaBuilder builder) {
27-
// TODO: it would be much better to not do use literal,
28-
// and let it be treated as a parameter, but we
29-
// we run into the usual bug with parameters in
30-
// manipulated SQM trees
31-
final Expression<U> literal = builder.literal( bound );
3226
return open
33-
? builder.greaterThan( path, literal )
34-
: builder.greaterThanOrEqualTo( path, literal );
27+
? builder.greaterThan( path, bound )
28+
: builder.greaterThanOrEqualTo( path, bound );
3529
}
3630

3731
@Override

hibernate-core/src/main/java/org/hibernate/query/range/UpperBound.java

+2-8
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
package org.hibernate.query.range;
66

77
import jakarta.persistence.criteria.CriteriaBuilder;
8-
import jakarta.persistence.criteria.Expression;
98
import jakarta.persistence.criteria.Path;
109
import jakarta.persistence.criteria.Predicate;
1110

@@ -23,14 +22,9 @@ record UpperBound<U extends Comparable<U>>(U bound, boolean open) implements Ran
2322

2423
@Override
2524
public Predicate toPredicate(Path<? extends U> path, CriteriaBuilder builder) {
26-
// TODO: it would be much better to not do use literal,
27-
// and let it be treated as a parameter, but we
28-
// we run into the usual bug with parameters in
29-
// manipulated SQM trees
30-
final Expression<U> literal = builder.literal( bound );
3125
return open
32-
? builder.lessThan( path, literal )
33-
: builder.lessThanOrEqualTo( path, literal );
26+
? builder.lessThan( path, bound )
27+
: builder.lessThanOrEqualTo( path, bound );
3428
}
3529

3630
@Override @SuppressWarnings("unchecked")

hibernate-core/src/main/java/org/hibernate/query/range/Value.java

+1-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
package org.hibernate.query.range;
66

77
import jakarta.persistence.criteria.CriteriaBuilder;
8-
import jakarta.persistence.criteria.Expression;
98
import jakarta.persistence.criteria.Path;
109
import jakarta.persistence.criteria.Predicate;
1110
import org.hibernate.internal.util.ReflectHelper;
@@ -24,12 +23,7 @@ record Value<U>(U value) implements Range<U> {
2423

2524
@Override
2625
public Predicate toPredicate(Path<? extends U> path, CriteriaBuilder builder) {
27-
// TODO: it would be much better to not do use literal,
28-
// and let it be treated as a parameter, but we
29-
// we run into the usual bug with parameters in
30-
// manipulated SQM trees
31-
final Expression<U> literal = builder.literal( value );
32-
return path.equalTo( literal );
26+
return path.equalTo( value );
3327
}
3428

3529
@Override

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

+66-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
*/
55
package org.hibernate.query.sqm.internal;
66

7+
import jakarta.persistence.TemporalType;
78
import org.hibernate.HibernateException;
89
import org.hibernate.engine.spi.SharedSessionContractImplementor;
910
import org.hibernate.graph.spi.AppliedGraph;
11+
import org.hibernate.query.BindableType;
1012
import org.hibernate.query.IllegalSelectQueryException;
1113
import org.hibernate.query.KeyedPage;
1214
import org.hibernate.query.KeyedResultList;
@@ -23,9 +25,15 @@
2325
import org.hibernate.query.spi.ParameterMetadataImplementor;
2426
import org.hibernate.query.spi.QueryEngine;
2527
import org.hibernate.query.spi.QueryOptions;
28+
import org.hibernate.query.spi.QueryParameterBinding;
29+
import org.hibernate.query.spi.QueryParameterBindings;
2630
import org.hibernate.query.spi.SelectQueryPlan;
31+
import org.hibernate.query.sqm.SqmQuerySource;
2732
import org.hibernate.query.sqm.spi.NamedSqmQueryMemento;
2833
import org.hibernate.query.sqm.tree.SqmStatement;
34+
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
35+
import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper;
36+
import org.hibernate.query.sqm.tree.expression.SqmParameter;
2937
import org.hibernate.query.sqm.tree.from.SqmRoot;
3038
import org.hibernate.query.sqm.tree.select.SqmQueryGroup;
3139
import org.hibernate.query.sqm.tree.select.SqmQueryPart;
@@ -118,9 +126,64 @@ private SqmSelectStatement<R> getSqmSelectStatement() {
118126
}
119127
}
120128

129+
protected void copyParameterBindings(QueryParameterBindings oldParameterBindings) {
130+
final QueryParameterBindings parameterBindings = getQueryParameterBindings();
131+
oldParameterBindings.visitBindings( (queryParameter, binding) -> {
132+
if ( binding.isBound() ) {
133+
//noinspection unchecked
134+
final QueryParameterBinding<Object> newBinding = (QueryParameterBinding<Object>) parameterBindings.getBinding( queryParameter );
135+
//noinspection unchecked
136+
final BindableType<Object> bindType = (BindableType<Object>) binding.getBindType();
137+
final TemporalType explicitTemporalPrecision = binding.getExplicitTemporalPrecision();
138+
if ( binding.isMultiValued() ) {
139+
if ( explicitTemporalPrecision != null ) {
140+
newBinding.setBindValues( binding.getBindValues(), explicitTemporalPrecision, getSessionFactory().getTypeConfiguration() );
141+
}
142+
else if ( bindType != null ) {
143+
newBinding.setBindValues( binding.getBindValues(), bindType );
144+
}
145+
else {
146+
newBinding.setBindValues( binding.getBindValues() );
147+
}
148+
}
149+
else {
150+
if ( explicitTemporalPrecision != null ) {
151+
newBinding.setBindValue( binding.getBindValue(), explicitTemporalPrecision );
152+
}
153+
else if ( bindType != null ) {
154+
newBinding.setBindValue( binding.getBindValue(), bindType );
155+
}
156+
else {
157+
newBinding.setBindValue( binding.getBindValue() );
158+
}
159+
}
160+
}
161+
} );
162+
163+
// Parameters might be created through HibernateCriteriaBuilder.value which we need to bind here
164+
for ( SqmParameter<?> sqmParameter : getDomainParameterXref().getParameterResolutions().getSqmParameters() ) {
165+
if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper<?> wrapper ) {
166+
bindCriteriaParameter( wrapper );
167+
}
168+
}
169+
}
170+
171+
protected <T> void bindCriteriaParameter(SqmJpaCriteriaParameterWrapper<T> sqmParameter) {
172+
final JpaCriteriaParameter<T> criteriaParameter = sqmParameter.getJpaCriteriaParameter();
173+
final T value = criteriaParameter.getValue();
174+
// We don't set a null value, unless the type is also null which
175+
// is the case when using HibernateCriteriaBuilder.value
176+
if ( value != null || criteriaParameter.getNodeType() == null ) {
177+
// Use the anticipated type for binding the value if possible
178+
getQueryParameterBindings()
179+
.getBinding( criteriaParameter )
180+
.setBindValue( value, criteriaParameter.getAnticipatedType() );
181+
}
182+
}
183+
121184
@Override
122185
public SelectionQuery<R> setOrder(List<? extends Order<? super R>> orderList) {
123-
final SqmSelectStatement<R> selectStatement = getSqmSelectStatement().copy( noParamCopyContext() );
186+
final SqmSelectStatement<R> selectStatement = getSqmSelectStatement().copy( noParamCopyContext( SqmQuerySource.CRITERIA ) );
124187
selectStatement.orderBy( orderList.stream().map( order -> sortSpecification( selectStatement, order ) )
125188
.collect( toList() ) );
126189
// TODO: when the QueryInterpretationCache can handle caching criteria queries,
@@ -133,7 +196,7 @@ public SelectionQuery<R> setOrder(List<? extends Order<? super R>> orderList) {
133196

134197
@Override
135198
public SelectionQuery<R> setOrder(Order<? super R> order) {
136-
final SqmSelectStatement<R> selectStatement = getSqmSelectStatement().copy( noParamCopyContext() );
199+
final SqmSelectStatement<R> selectStatement = getSqmSelectStatement().copy( noParamCopyContext( SqmQuerySource.CRITERIA ) );
137200
selectStatement.orderBy( sortSpecification( selectStatement, order ) );
138201
// TODO: when the QueryInterpretationCache can handle caching criteria queries,
139202
// simply cache the new SQM as if it were a criteria query, and remove this:
@@ -144,7 +207,7 @@ public SelectionQuery<R> setOrder(Order<? super R> order) {
144207

145208
@Override
146209
public SelectionQuery<R> addRestriction(Restriction<? super R> restriction) {
147-
final SqmSelectStatement<R> selectStatement = getSqmSelectStatement().copy( noParamCopyContext() );
210+
final SqmSelectStatement<R> selectStatement = getSqmSelectStatement().copy( noParamCopyContext( SqmQuerySource.CRITERIA ) );
148211
restriction.apply( selectStatement, selectStatement.<R>getRoot( 0, getExpectedResultType() ) );
149212
// TODO: when the QueryInterpretationCache can handle caching criteria queries,
150213
// simply cache the new SQM as if it were a criteria query, and remove this:

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

+13-17
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@
7070
import org.hibernate.query.sqm.tree.SqmStatement;
7171
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
7272
import org.hibernate.query.sqm.tree.domain.SqmPath;
73-
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
7473
import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper;
7574
import org.hibernate.query.sqm.tree.expression.SqmParameter;
7675
import org.hibernate.query.sqm.tree.from.SqmRoot;
@@ -121,13 +120,13 @@ public class QuerySqmImpl<R>
121120
implements SqmQueryImplementor<R>, InterpretationsKeySource, DomainQueryExecutionContext {
122121

123122
private final String hql;
124-
private final Object queryStringCacheKey;
123+
private Object queryStringCacheKey;
125124
private SqmStatement<R> sqm;
126125

127-
private final ParameterMetadataImplementor parameterMetadata;
128-
private final DomainParameterXref domainParameterXref;
126+
private ParameterMetadataImplementor parameterMetadata;
127+
private DomainParameterXref domainParameterXref;
129128

130-
private final QueryParameterBindings parameterBindings;
129+
private QueryParameterBindings parameterBindings;
131130

132131
private final Class<R> resultType;
133132
private final TupleMetadata tupleMetadata;
@@ -254,18 +253,6 @@ else if ( sqm instanceof AbstractSqmDmlStatement<R> update ) {
254253
tupleMetadata = buildTupleMetadata( criteria, expectedResultType );
255254
}
256255

257-
private <T> void bindCriteriaParameter(SqmJpaCriteriaParameterWrapper<T> sqmParameter) {
258-
final JpaCriteriaParameter<T> jpaCriteriaParameter = sqmParameter.getJpaCriteriaParameter();
259-
final T value = jpaCriteriaParameter.getValue();
260-
// We don't set a null value, unless the type is also null which
261-
// is the case when using HibernateCriteriaBuilder.value
262-
if ( value != null || jpaCriteriaParameter.getNodeType() == null ) {
263-
// Use the anticipated type for binding the value if possible
264-
getQueryParameterBindings().getBinding( jpaCriteriaParameter )
265-
.setBindValue( value, jpaCriteriaParameter.getAnticipatedType() );
266-
}
267-
}
268-
269256
@Override
270257
public TupleMetadata getTupleMetadata() {
271258
return tupleMetadata;
@@ -289,6 +276,15 @@ public SqmStatement<R> getSqmStatement() {
289276
@Override
290277
protected void setSqmStatement(SqmSelectStatement<R> sqm) {
291278
this.sqm = sqm;
279+
this.queryStringCacheKey = sqm;
280+
281+
final QueryParameterBindings oldParameterBindings = parameterBindings;
282+
domainParameterXref = DomainParameterXref.from( sqm );
283+
parameterMetadata = !domainParameterXref.hasParameters()
284+
? ParameterMetadataImpl.EMPTY
285+
: new ParameterMetadataImpl( domainParameterXref.getQueryParameters() );
286+
parameterBindings = parameterMetadata.createBindings( getSessionFactory() );
287+
copyParameterBindings( oldParameterBindings );
292288
}
293289

294290
@Override

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

+13-18
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
import org.hibernate.query.sqm.internal.SqmInterpretationsKey.InterpretationsKeySource;
5656
import org.hibernate.query.sqm.spi.SqmSelectionQueryImplementor;
5757
import org.hibernate.query.sqm.tree.SqmCopyContext;
58-
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
5958
import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper;
6059
import org.hibernate.query.sqm.tree.expression.SqmParameter;
6160
import org.hibernate.query.sqm.tree.select.SqmQueryPart;
@@ -89,12 +88,12 @@
8988
public class SqmSelectionQueryImpl<R> extends AbstractSqmSelectionQuery<R>
9089
implements SqmSelectionQueryImplementor<R>, InterpretationsKeySource {
9190
private final String hql;
92-
private final Object queryStringCacheKey;
91+
private Object queryStringCacheKey;
9392
private SqmSelectStatement<R> sqm;
9493

95-
private final ParameterMetadataImplementor parameterMetadata;
96-
private final DomainParameterXref domainParameterXref;
97-
private final QueryParameterBindings parameterBindings;
94+
private ParameterMetadataImplementor parameterMetadata;
95+
private DomainParameterXref domainParameterXref;
96+
private QueryParameterBindings parameterBindings;
9897

9998
private final Class<R> expectedResultType;
10099
private final Class<?> resultType;
@@ -330,19 +329,6 @@ else if ( expectedResultType != null ) {
330329
}
331330
}
332331

333-
private <T> void bindCriteriaParameter(SqmJpaCriteriaParameterWrapper<T> sqmParameter) {
334-
final JpaCriteriaParameter<T> criteriaParameter = sqmParameter.getJpaCriteriaParameter();
335-
final T value = criteriaParameter.getValue();
336-
// We don't set a null value, unless the type is also null which
337-
// is the case when using HibernateCriteriaBuilder.value
338-
if ( value != null || criteriaParameter.getNodeType() == null ) {
339-
// Use the anticipated type for binding the value if possible
340-
getQueryParameterBindings()
341-
.getBinding( criteriaParameter )
342-
.setBindValue( value, criteriaParameter.getAnticipatedType() );
343-
}
344-
}
345-
346332
@Override
347333
public TupleMetadata getTupleMetadata() {
348334
return tupleMetadata;
@@ -356,6 +342,15 @@ public SqmSelectStatement<R> getSqmStatement() {
356342
@Override
357343
protected void setSqmStatement(SqmSelectStatement<R> sqm) {
358344
this.sqm = sqm;
345+
this.queryStringCacheKey = sqm;
346+
347+
final QueryParameterBindings oldParameterBindings = parameterBindings;
348+
domainParameterXref = DomainParameterXref.from( sqm );
349+
parameterMetadata = !domainParameterXref.hasParameters()
350+
? ParameterMetadataImpl.EMPTY
351+
: new ParameterMetadataImpl( domainParameterXref.getQueryParameters() );
352+
parameterBindings = parameterMetadata.createBindings( getSessionFactory() );
353+
copyParameterBindings( oldParameterBindings );
359354
}
360355

361356
@Override

hibernate-core/src/main/java/org/hibernate/query/sqm/tree/jpa/ParameterCollector.java

+13
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.hibernate.query.sqm.tree.SqmVisitableNode;
1919
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
2020
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
21+
import org.hibernate.query.sqm.tree.expression.SqmAliasedNodeRef;
2122
import org.hibernate.query.sqm.tree.expression.SqmCaseSearched;
2223
import org.hibernate.query.sqm.tree.expression.SqmCaseSimple;
2324
import org.hibernate.query.sqm.tree.expression.SqmExpression;
@@ -37,6 +38,7 @@
3738
import org.hibernate.query.sqm.tree.predicate.SqmNullnessPredicate;
3839
import org.hibernate.query.sqm.tree.predicate.SqmTruthnessPredicate;
3940
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
41+
import org.hibernate.query.sqm.tree.select.SqmSortSpecification;
4042

4143
/**
4244
* @author Steve Ebersole
@@ -341,4 +343,15 @@ public Object visitInSubQueryPredicate(SqmInSubQueryPredicate<?> predicate) {
341343
withTypeInference( predicate.getTestExpression(), predicate.getSubQueryExpression() );
342344
return predicate;
343345
}
346+
347+
@Override
348+
public Object visitSortSpecification(SqmSortSpecification sortSpecification) {
349+
if ( sortSpecification.getSortExpression() instanceof SqmAliasedNodeRef ) {
350+
// Ignore aliases
351+
return sortSpecification;
352+
}
353+
else {
354+
return super.visitSortSpecification( sortSpecification );
355+
}
356+
}
344357
}

0 commit comments

Comments
 (0)