diff --git a/hibernate-core/src/main/java/org/hibernate/query/KeyedPage.java b/hibernate-core/src/main/java/org/hibernate/query/KeyedPage.java index 19afb3f5ed..345951c79a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/KeyedPage.java +++ b/hibernate-core/src/main/java/org/hibernate/query/KeyedPage.java @@ -7,6 +7,7 @@ package org.hibernate.query; import org.hibernate.Incubating; +import org.hibernate.Internal; import java.util.List; @@ -75,7 +76,8 @@ public class KeyedPage { return key; } - public static KeyedPage forKey(List> keyDefinition, Page page, List> key) { - return new KeyedPage<>( keyDefinition, page, key ); + @Internal + public KeyedPage nextPage(List> keyOfLastResult) { + return new KeyedPage<>( keyDefinition, page, keyOfLastResult ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AbstractSqmSelectionQuery.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AbstractSqmSelectionQuery.java index e3a00d9034..dbdea8ddec 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AbstractSqmSelectionQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/AbstractSqmSelectionQuery.java @@ -160,37 +160,45 @@ abstract class AbstractSqmSelectionQuery extends AbstractSelectionQuery { } } - private SqmSelectStatement> keyed(List> keyDefinition, List> keyValues) { + private SqmSelectStatement> keyedQuery( + List> keyDefinition, List> keyValues) { @SuppressWarnings("unchecked") final SqmSelectStatement> sqm = (SqmSelectStatement>) getSqmSelectStatement().copy( noParamCopyContext() ); final NodeBuilder builder = sqm.nodeBuilder(); final SqmQuerySpec querySpec = sqm.getQuerySpec(); - final SqmWhereClause whereClause = querySpec.getWhereClause(); final List> items = querySpec.getSelectClause().getSelectionItems(); - final List> newItems = new ArrayList<>(); if ( items.size() == 1 ) { final JpaSelection selected = items.get(0); - for ( int i = 0; i < keyDefinition.size(); i++ ) { - // ordering by an attribute of the returned entity - if ( selected instanceof SqmRoot ) { + if ( selected instanceof SqmRoot ) { + final List> newItems = new ArrayList<>(); + final SqmFrom root = (SqmFrom) selected; + SqmPredicate restriction = querySpec.getRestriction(); + if ( restriction==null && keyValues != null ) { + restriction = builder.disjunction(); + } + for ( int i = 0; i < keyDefinition.size(); i++ ) { + // ordering by an attribute of the returned entity final Order key = keyDefinition.get(i); - final SqmFrom root = (SqmFrom) selected; if ( keyValues != null ) { @SuppressWarnings("rawtypes") final Comparable keyValue = keyValues.get(i); - whereClause.applyPredicate( keyPredicate( root, key, keyValue, builder ) ); + restriction = builder.or( restriction, + keyPredicate( root, key, keyValue, newItems, keyValues, builder ) ); } newItems.add( root.get( key.getAttributeName() ) ); } - else { - throw new IllegalQueryOperationException("Select item was not an entity type"); + sqm.select( builder.construct((Class) KeyedResult.class, + asList(selected, builder.construct(List.class, newItems)) ) ); + if ( keyValues != null ) { + sqm.where( restriction ); } + return sqm; + } + else { + throw new IllegalQueryOperationException("Select item was not an entity type"); } - sqm.select( builder.construct((Class) KeyedResult.class, - asList(selected, builder.construct(List.class, newItems)) ) ); - return sqm; } else { throw new IllegalQueryOperationException("Query has multiple items in the select list"); @@ -198,7 +206,9 @@ abstract class AbstractSqmSelectionQuery extends AbstractSelectionQuery { } private > SqmPredicate keyPredicate( - SqmFrom selected, Order key, C keyValue, NodeBuilder builder) { + SqmFrom selected, Order key, C keyValue, + List> previousKeys, List> keyValues, + NodeBuilder builder) { if ( !key.getEntityClass().isAssignableFrom( selected.getJavaType() ) ) { throw new IllegalQueryOperationException("Select item was of wrong entity type"); } @@ -208,14 +218,22 @@ abstract class AbstractSqmSelectionQuery extends AbstractSelectionQuery { // final Class valueClass = (Class) keyValue.getClass(); // final JpaParameterExpression parameter = builder.parameter(valueClass); // setParameter( parameter, keyValue ); + SqmPredicate predicate; switch ( key.getDirection() ) { case ASCENDING: - return builder.greaterThan( path, keyValue ); + predicate = builder.greaterThan( path, keyValue ); + break; case DESCENDING: - return builder.lessThan( path, keyValue ); + predicate = builder.lessThan( path, keyValue ); + break; default: throw new AssertionFailure("Unrecognized key direction"); } + for ( int i = 0; i < previousKeys.size(); i++ ) { + final SqmPath keyPath = previousKeys.get(i); + predicate = builder.and( predicate, keyPath.equalTo( keyValues.get(i) ) ); + } + return predicate; } @Override @@ -232,18 +250,11 @@ abstract class AbstractSqmSelectionQuery extends AbstractSelectionQuery { if ( key == null ) { setFirstResult( page.getFirstResult() ); } - else { - keyed( keyDefinition, key ); - } - final ConcreteSqmSelectQueryPlan> plan = - buildConcreteQueryPlan( keyed( keyDefinition, key ), - null, null, - getQueryOptions() ); - final List> executed = plan.performList(this); - final KeyedPage nextPage = - KeyedPage.forKey(keyDefinition, page.next(), - getKeyOfLastResult(executed, key)); + final List> executed = + buildConcreteQueryPlan( keyedQuery( keyDefinition, key ), getQueryOptions() ) + .performList(this); + final KeyedPage nextPage = keyedPage.nextPage( getKeyOfLastResult( executed, key ) ); return new KeyedResultList<>( collectResultList( executed ), keyedPage, nextPage ); } @@ -301,4 +312,8 @@ abstract class AbstractSqmSelectionQuery extends AbstractSelectionQuery { queryOptions ); } + + private SelectQueryPlan buildConcreteQueryPlan(SqmSelectStatement sqmStatement, QueryOptions options) { + return buildConcreteQueryPlan( sqmStatement, null, null, options ); + } }