Skip to content

Commit ddadad2

Browse files
committed
HHH-18596 Get rid of ValueHandlingMode hack in query pagination
1 parent 0e5846b commit ddadad2

16 files changed

+158
-66
lines changed

hibernate-core/src/main/java/org/hibernate/query/spi/AbstractCommonQueryContract.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,16 @@
9494
*/
9595
public abstract class AbstractCommonQueryContract implements CommonQueryContract {
9696
private final SharedSessionContractImplementor session;
97-
private final QueryOptionsImpl queryOptions = new QueryOptionsImpl();
97+
private final QueryOptionsImpl queryOptions;
9898

9999
public AbstractCommonQueryContract(SharedSessionContractImplementor session) {
100100
this.session = session;
101+
this.queryOptions = new QueryOptionsImpl();
102+
}
103+
104+
protected AbstractCommonQueryContract(AbstractCommonQueryContract original) {
105+
this.session = original.session;
106+
this.queryOptions = original.queryOptions;
101107
}
102108

103109
public SharedSessionContractImplementor getSession() {

hibernate-core/src/main/java/org/hibernate/query/spi/AbstractSelectionQuery.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ public AbstractSelectionQuery(SharedSessionContractImplementor session) {
8080
super( session );
8181
}
8282

83+
protected AbstractSelectionQuery(AbstractSelectionQuery<?> original) {
84+
super( original );
85+
this.sessionFlushMode = original.sessionFlushMode;
86+
this.sessionCacheMode = original.sessionCacheMode;
87+
}
88+
8389
protected void applyOptions(NamedQueryMemento<?> memento) {
8490
if ( memento.getHints() != null ) {
8591
memento.getHints().forEach( this::applyHint );

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import java.util.Map;
1616
import java.util.Set;
1717

18-
import org.hibernate.Internal;
1918
import org.hibernate.jpa.spi.JpaCompliance;
2019
import org.hibernate.metamodel.model.domain.JpaMetamodel;
2120
import org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode;
@@ -33,7 +32,6 @@
3332
import org.hibernate.query.criteria.JpaSelection;
3433
import org.hibernate.query.criteria.JpaSimpleCase;
3534
import org.hibernate.query.criteria.JpaWindow;
36-
import org.hibernate.query.criteria.ValueHandlingMode;
3735
import org.hibernate.query.spi.QueryEngine;
3836
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
3937
import org.hibernate.query.sqm.tree.domain.SqmBagJoin;
@@ -91,9 +89,6 @@ public interface NodeBuilder extends HibernateCriteriaBuilder, BindingContext {
9189

9290
QueryEngine getQueryEngine();
9391

94-
@Internal
95-
ValueHandlingMode setCriteriaValueHandlingMode(ValueHandlingMode criteriaValueHandlingMode);
96-
9792
<R> SqmTuple<R> tuple(
9893
Class<R> tupleType,
9994
SqmExpression<?>... expressions);

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

Lines changed: 6 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import org.hibernate.query.QueryLogging;
1616
import org.hibernate.query.SelectionQuery;
1717
import org.hibernate.query.criteria.JpaSelection;
18-
import org.hibernate.query.criteria.ValueHandlingMode;
1918
import org.hibernate.query.hql.internal.NamedHqlQueryMementoImpl;
2019
import org.hibernate.query.hql.internal.QuerySplitter;
2120
import org.hibernate.query.named.NamedQueryMemento;
@@ -25,7 +24,6 @@
2524
import org.hibernate.query.spi.QueryEngine;
2625
import org.hibernate.query.spi.QueryOptions;
2726
import org.hibernate.query.spi.SelectQueryPlan;
28-
import org.hibernate.query.sqm.NodeBuilder;
2927
import org.hibernate.query.sqm.spi.NamedSqmQueryMemento;
3028
import org.hibernate.query.sqm.tree.SqmStatement;
3129
import org.hibernate.query.sqm.tree.from.SqmRoot;
@@ -48,7 +46,6 @@
4846
import static java.util.stream.Collectors.toList;
4947
import static org.hibernate.cfg.QuerySettings.FAIL_ON_PAGINATION_OVER_COLLECTION_FETCH;
5048
import static org.hibernate.query.KeyedPage.KeyInterpretation.KEY_OF_FIRST_ON_NEXT_PAGE;
51-
import static org.hibernate.query.sqm.internal.KeyBasedPagination.paginate;
5249
import static org.hibernate.query.sqm.internal.KeyedResult.collectKeys;
5350
import static org.hibernate.query.sqm.internal.KeyedResult.collectResults;
5451
import static org.hibernate.query.sqm.internal.SqmUtil.isHqlTuple;
@@ -65,6 +62,10 @@ abstract class AbstractSqmSelectionQuery<R> extends AbstractSelectionQuery<R> {
6562
super(session);
6663
}
6764

65+
AbstractSqmSelectionQuery(AbstractSqmSelectionQuery<?> original) {
66+
super( original );
67+
}
68+
6869
protected int max(boolean hasLimit, SqmSelectStatement<?> sqmStatement, List<R> list) {
6970
return !hasLimit || getQueryOptions().getLimit().getMaxRows() == null
7071
? getMaxRows( sqmStatement, list.size() )
@@ -154,46 +155,14 @@ public SelectionQuery<R> setPage(Page page) {
154155
return this;
155156
}
156157

157-
private SqmSelectStatement<KeyedResult<R>> paginateQuery(
158-
List<Order<? super R>> keyDefinition, List<Comparable<?>> keyValues) {
159-
@SuppressWarnings("unchecked")
160-
final SqmSelectStatement<KeyedResult<R>> sqm =
161-
(SqmSelectStatement<KeyedResult<R>>)
162-
getSqmSelectStatement().copy( noParamCopyContext() );
163-
final NodeBuilder builder = sqm.nodeBuilder();
164-
//TODO: find a better way handle parameters
165-
final ValueHandlingMode valueHandlingMode = builder.setCriteriaValueHandlingMode(ValueHandlingMode.INLINE);
166-
try {
167-
return paginate( keyDefinition, keyValues, sqm, builder );
168-
}
169-
finally {
170-
builder.setCriteriaValueHandlingMode( valueHandlingMode );
171-
}
172-
}
173-
174-
175158
@Override
176159
public KeyedResultList<R> getKeyedResultList(KeyedPage<R> keyedPage) {
177160
if ( keyedPage == null ) {
178161
throw new IllegalArgumentException( "KeyedPage was null" );
179162
}
163+
final List<KeyedResult<R>> results = new SqmSelectionQueryImpl<KeyedResult<R>>( this, keyedPage )
164+
.getResultList();
180165
final Page page = keyedPage.getPage();
181-
final List<Comparable<?>> key = keyedPage.getKey();
182-
final List<Order<? super R>> keyDefinition = keyedPage.getKeyDefinition();
183-
final List<Order<? super R>> appliedKeyDefinition =
184-
keyedPage.getKeyInterpretation() == KEY_OF_FIRST_ON_NEXT_PAGE
185-
? Order.reverse( keyDefinition ) : keyDefinition;
186-
187-
setMaxResults( page.getMaxResults() + 1 );
188-
if ( key == null ) {
189-
setFirstResult( page.getFirstResult() );
190-
}
191-
192-
// getQueryOptions().setQueryPlanCachingEnabled( false );
193-
final List<KeyedResult<R>> results =
194-
buildConcreteQueryPlan( paginateQuery( appliedKeyDefinition, key ), getQueryOptions() )
195-
.performList(this);
196-
197166
return new KeyedResultList<>(
198167
collectResults( results, page.getSize(), keyedPage.getKeyInterpretation() ),
199168
collectKeys( results, page.getSize() ),
@@ -277,10 +246,6 @@ protected <T> ConcreteSqmSelectQueryPlan<T> buildConcreteQueryPlan(
277246
);
278247
}
279248

280-
private <T> SelectQueryPlan<T> buildConcreteQueryPlan(SqmSelectStatement<T> sqmStatement, QueryOptions options) {
281-
return buildConcreteQueryPlan( sqmStatement, null, null, options );
282-
}
283-
284249
protected void applyOptions(NamedSqmQueryMemento<?> memento) {
285250
applyOptions( (NamedQueryMemento<?>) memento );
286251

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,6 @@ private static <C extends Comparable<? super C>> SqmPredicate keyPredicate(
116116
Expression<? extends C> key, C keyValue, SortDirection direction,
117117
List<SqmPath<?>> previousKeys, List<Comparable<?>> keyValues,
118118
NodeBuilder builder) {
119-
// TODO: use a parameter here and create a binding for it
120-
// @SuppressWarnings("unchecked")
121-
// final Class<C> valueClass = (Class<C>) keyValue.getClass();
122-
// final JpaParameterExpression<C> parameter = builder.parameter(valueClass);
123-
// setParameter( parameter, keyValue );
124119
SqmPredicate predicate;
125120
switch ( direction ) {
126121
case ASCENDING:

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55
package org.hibernate.query.sqm.internal;
66

7+
import org.hibernate.query.sqm.SqmQuerySource;
78
import org.hibernate.query.sqm.tree.expression.SqmParameter;
89

910
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -12,6 +13,13 @@
1213
* @author Marco Belladelli
1314
*/
1415
public class NoParamSqmCopyContext extends SimpleSqmCopyContext {
16+
public NoParamSqmCopyContext() {
17+
}
18+
19+
public NoParamSqmCopyContext(@Nullable SqmQuerySource querySource) {
20+
super( querySource );
21+
}
22+
1523
@Override
1624
public <T> @Nullable T getCopy(T original) {
1725
if ( original instanceof SqmParameter<?> ) {

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import java.util.IdentityHashMap;
88

9+
import org.hibernate.query.sqm.SqmQuerySource;
910
import org.hibernate.query.sqm.tree.SqmCopyContext;
1011

1112
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -15,6 +16,15 @@
1516
*/
1617
public class SimpleSqmCopyContext implements SqmCopyContext {
1718
private final IdentityHashMap<Object, Object> map = new IdentityHashMap<>();
19+
private final @Nullable SqmQuerySource querySource;
20+
21+
public SimpleSqmCopyContext() {
22+
this( null );
23+
}
24+
25+
public SimpleSqmCopyContext(@Nullable SqmQuerySource querySource) {
26+
this.querySource = querySource;
27+
}
1828

1929
@Override
2030
@SuppressWarnings( "unchecked" )
@@ -30,4 +40,9 @@ public <T> T registerCopy(T original, T copy) {
3040
}
3141
return copy;
3242
}
43+
44+
@Override
45+
public @Nullable SqmQuerySource getQuerySource() {
46+
return querySource;
47+
}
3348
}

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

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -242,13 +242,6 @@ public SqmCriteriaNodeBuilder(
242242
}
243243
}
244244

245-
@Override
246-
public ValueHandlingMode setCriteriaValueHandlingMode(ValueHandlingMode criteriaValueHandlingMode) {
247-
ValueHandlingMode current = this.criteriaValueHandlingMode;
248-
this.criteriaValueHandlingMode = criteriaValueHandlingMode;
249-
return current;
250-
}
251-
252245
@Override
253246
public JpaMetamodel getDomainModel() {
254247
return bindingContext.getJpaMetamodel();

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

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@
2929
import org.hibernate.engine.spi.SharedSessionContractImplementor;
3030
import org.hibernate.graph.spi.AppliedGraph;
3131
import org.hibernate.internal.util.collections.IdentitySet;
32+
import org.hibernate.metamodel.mapping.MappingModelExpressible;
3233
import org.hibernate.query.BindableType;
34+
import org.hibernate.query.KeyedPage;
35+
import org.hibernate.query.Order;
36+
import org.hibernate.query.Page;
3337
import org.hibernate.query.QueryParameter;
3438
import org.hibernate.query.criteria.internal.NamedCriteriaQueryMementoImpl;
3539
import org.hibernate.query.hql.internal.NamedHqlQueryMementoImpl;
@@ -41,9 +45,11 @@
4145
import org.hibernate.query.spi.ParameterMetadataImplementor;
4246
import org.hibernate.query.spi.QueryInterpretationCache;
4347
import org.hibernate.query.spi.QueryOptions;
48+
import org.hibernate.query.spi.QueryParameterBinding;
4449
import org.hibernate.query.spi.QueryParameterBindings;
4550
import org.hibernate.query.spi.ScrollableResultsImplementor;
4651
import org.hibernate.query.spi.SelectQueryPlan;
52+
import org.hibernate.query.sqm.SqmQuerySource;
4753
import org.hibernate.query.sqm.SqmSelectionQuery;
4854
import org.hibernate.query.sqm.internal.SqmInterpretationsKey.InterpretationsKeySource;
4955
import org.hibernate.query.sqm.spi.SqmSelectionQueryImplementor;
@@ -68,9 +74,12 @@
6874
import static org.hibernate.jpa.LegacySpecHints.HINT_JAVAEE_CACHE_STORE_MODE;
6975
import static org.hibernate.jpa.SpecHints.HINT_SPEC_CACHE_RETRIEVE_MODE;
7076
import static org.hibernate.jpa.SpecHints.HINT_SPEC_CACHE_STORE_MODE;
77+
import static org.hibernate.query.KeyedPage.KeyInterpretation.KEY_OF_FIRST_ON_NEXT_PAGE;
7178
import static org.hibernate.query.spi.SqlOmittingQueryOptions.omitSqlQueryOptions;
79+
import static org.hibernate.query.sqm.internal.KeyBasedPagination.paginate;
7280
import static org.hibernate.query.sqm.internal.SqmInterpretationsKey.createInterpretationsKey;
7381
import static org.hibernate.query.sqm.internal.SqmUtil.isSelectionAssignableToResultType;
82+
import static org.hibernate.query.sqm.tree.SqmCopyContext.noParamCopyContext;
7483

7584
/**
7685
* @author Steve Ebersole
@@ -178,6 +187,87 @@ public SqmSelectionQueryImpl(
178187
this.tupleMetadata = buildTupleMetadata( sqm, expectedResultType );
179188
}
180189

190+
<E> SqmSelectionQueryImpl(AbstractSqmSelectionQuery<?> original, KeyedPage<E> keyedPage) {
191+
super( original );
192+
193+
final Page page = keyedPage.getPage();
194+
final List<Comparable<?>> key = keyedPage.getKey();
195+
final List<Order<? super E>> keyDefinition = keyedPage.getKeyDefinition();
196+
final List<Order<? super E>> appliedKeyDefinition =
197+
keyedPage.getKeyInterpretation() == KEY_OF_FIRST_ON_NEXT_PAGE
198+
? Order.reverse( keyDefinition ) : keyDefinition;
199+
200+
//noinspection unchecked
201+
this.sqm = (SqmSelectStatement<R>) paginate(
202+
appliedKeyDefinition,
203+
key,
204+
// Change the query source to CRITERIA, because we will change the query and introduce parameters
205+
(SqmSelectStatement<KeyedResult<E>>) original.getSqmStatement()
206+
.copy( noParamCopyContext( SqmQuerySource.CRITERIA ) ),
207+
original.getSqmStatement().nodeBuilder()
208+
);
209+
this.hql = CRITERIA_HQL_STRING;
210+
211+
this.domainParameterXref = DomainParameterXref.from( sqm );
212+
this.parameterMetadata = domainParameterXref.hasParameters()
213+
? new ParameterMetadataImpl( domainParameterXref.getQueryParameters() )
214+
: ParameterMetadataImpl.EMPTY;
215+
216+
// Just use the original parameter bindings since this object is never going to be mutated
217+
this.parameterBindings = parameterMetadata.createBindings( original.getSession().getSessionFactory() );
218+
// Don't remove this cast. This is here to work around this bug: https://bugs.openjdk.org/browse/JDK-8340443
219+
(( DomainQueryExecutionContext) original ).getQueryParameterBindings().visitBindings(
220+
(parameter, binding) -> {
221+
//noinspection unchecked
222+
final QueryParameterBinding<Object> parameterBinding =
223+
(QueryParameterBinding<Object>) this.parameterBindings.getBinding( parameter );
224+
//noinspection unchecked
225+
final BindableType<Object> bindType = (BindableType<Object>) binding.getBindType();
226+
final TemporalType explicitTemporalPrecision = binding.getExplicitTemporalPrecision();
227+
if ( explicitTemporalPrecision != null ) {
228+
if ( binding.isMultiValued() ) {
229+
parameterBinding.setBindValues(
230+
binding.getBindValues(),
231+
explicitTemporalPrecision,
232+
getSessionFactory().getTypeConfiguration()
233+
);
234+
}
235+
else {
236+
parameterBinding.setBindValue( binding.getBindValue(), explicitTemporalPrecision );
237+
}
238+
}
239+
else {
240+
if ( binding.isMultiValued() ) {
241+
parameterBinding.setBindValues( binding.getBindValues(), bindType );
242+
}
243+
else {
244+
parameterBinding.setBindValue( binding.getBindValue(), bindType );
245+
}
246+
}
247+
//noinspection unchecked
248+
parameterBinding.setType( (MappingModelExpressible<Object>) binding.getType() );
249+
}
250+
);
251+
252+
// Parameters might be created through HibernateCriteriaBuilder.value which we need to bind here
253+
for ( SqmParameter<?> sqmParameter : domainParameterXref.getParameterResolutions().getSqmParameters() ) {
254+
if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper<?> ) {
255+
bindCriteriaParameter( (SqmJpaCriteriaParameterWrapper<?>) sqmParameter );
256+
}
257+
}
258+
259+
//noinspection unchecked
260+
this.expectedResultType = (Class<R>) KeyedResult.class;
261+
this.resultType = determineResultType( sqm, expectedResultType );
262+
this.tupleMetadata = null;
263+
264+
setMaxResults( page.getMaxResults() + 1 );
265+
if ( key == null ) {
266+
setFirstResult( page.getFirstResult() );
267+
}
268+
}
269+
270+
181271
private static Class<?> determineResultType(SqmSelectStatement<?> sqm, Class<?> expectedResultType) {
182272
final List<SqmSelection<?>> selections = sqm.getQuerySpec().getSelectClause().getSelections();
183273
if ( selections.size() == 1 ) {

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package org.hibernate.query.sqm.tree;
66

77
import org.hibernate.Incubating;
8+
import org.hibernate.query.sqm.SqmQuerySource;
89
import org.hibernate.query.sqm.internal.NoParamSqmCopyContext;
910
import org.hibernate.query.sqm.internal.SimpleSqmCopyContext;
1011

@@ -29,11 +30,30 @@ default boolean copyFetchedFlag() {
2930
return true;
3031
}
3132

33+
/**
34+
* Returns the query source to use for copied queries.
35+
* {@code null} means, that the original query source should be retained.
36+
*
37+
* @since 7.0
38+
*/
39+
@Incubating
40+
default @Nullable SqmQuerySource getQuerySource() {
41+
return null;
42+
}
43+
3244
static SqmCopyContext simpleContext() {
3345
return new SimpleSqmCopyContext();
3446
}
3547

48+
static SqmCopyContext simpleContext(SqmQuerySource querySource) {
49+
return new SimpleSqmCopyContext( querySource );
50+
}
51+
3652
static SqmCopyContext noParamCopyContext() {
3753
return new NoParamSqmCopyContext();
3854
}
55+
56+
static SqmCopyContext noParamCopyContext(SqmQuerySource querySource) {
57+
return new NoParamSqmCopyContext( querySource );
58+
}
3959
}

0 commit comments

Comments
 (0)