HHH-17779 misc improvements to key-based pagination

This commit is contained in:
Gavin King 2024-02-28 10:35:49 +01:00
parent ecb88be84a
commit 1eff3c990b
5 changed files with 57 additions and 18 deletions

View File

@ -105,4 +105,9 @@ public class KeyedPage<R> {
public KeyedPage<R> nextPage(List<Comparable<?>> keyOfLastResult) {
return new KeyedPage<>( keyDefinition, page.next(), keyOfLastResult );
}
@Internal
public KeyedPage<R> withKey(List<Comparable<?>> keyOfLastResult) {
return new KeyedPage<>( keyDefinition, page, keyOfLastResult );
}
}

View File

@ -27,11 +27,13 @@ import java.util.List;
@Incubating
public class KeyedResultList<R> {
private final List<R> resultList;
private final List<List<?>> keyList;
private final KeyedPage<R> page;
private final KeyedPage<R> nextPage;
public KeyedResultList(List<R> resultList, KeyedPage<R> page, KeyedPage<R> nextPage) {
public KeyedResultList(List<R> resultList, List<List<?>> keyList, KeyedPage<R> page, KeyedPage<R> nextPage) {
this.resultList = resultList;
this.keyList = keyList;
this.page = page;
this.nextPage = nextPage;
}
@ -43,6 +45,13 @@ public class KeyedResultList<R> {
return resultList;
}
/**
* The keys of the results, in order.
*/
public List<List<?>> getKeyList() {
return keyList;
}
/**
* The {@linkplain Page#getSize() size} and
* approximate {@linkplain Page#getNumber()

View File

@ -33,6 +33,8 @@ import java.util.List;
import static java.util.stream.Collectors.toList;
import static org.hibernate.cfg.QuerySettings.FAIL_ON_PAGINATION_OVER_COLLECTION_FETCH;
import static org.hibernate.query.sqm.internal.KeyBasedPagination.paginate;
import static org.hibernate.query.sqm.internal.KeyedResult.collectKeys;
import static org.hibernate.query.sqm.internal.KeyedResult.collectResults;
import static org.hibernate.query.sqm.internal.SqmUtil.sortSpecification;
import static org.hibernate.query.sqm.tree.SqmCopyContext.noParamCopyContext;
@ -150,7 +152,7 @@ abstract class AbstractSqmSelectionQuery<R> extends AbstractSelectionQuery<R> {
@Override
public KeyedResultList<R> getKeyedResultList(KeyedPage<R> keyedPage) {
if ( keyedPage == null ) {
throw new IllegalStateException( "KeyedPage was not set" );
throw new IllegalStateException( "KeyedPage was not null" );
}
final Page page = keyedPage.getPage();
final List<Order<? super R>> keyDefinition = keyedPage.getKeyDefinition();
@ -163,21 +165,16 @@ abstract class AbstractSqmSelectionQuery<R> extends AbstractSelectionQuery<R> {
}
// getQueryOptions().setQueryPlanCachingEnabled( false );
final List<KeyedResult<R>> executed =
final List<KeyedResult<R>> results =
buildConcreteQueryPlan( paginateQuery( keyDefinition, key ), getQueryOptions() )
.performList(this);
return new KeyedResultList<>( collectResults( executed, page.getSize() ),
keyedPage, getNextPage( keyedPage, executed ) );
}
private static <R> List<R> collectResults(List<KeyedResult<R>> executed, int pageSize) {
final int size = executed.size();
final List<R> resultList = new ArrayList<>( size );
for (int i = 0; i < size && i < pageSize; i++) {
resultList.add( executed.get(i).getResult() );
}
return resultList;
return new KeyedResultList<>(
collectResults( results, page.getSize() ),
collectKeys( results, page.getSize() ),
keyedPage,
getNextPage( keyedPage, results )
);
}
private static <R> KeyedPage<R> getNextPage(KeyedPage<R> keyedPage, List<KeyedResult<R>> executed) {

View File

@ -88,11 +88,16 @@ public class KeyBasedPagination {
NodeBuilder builder) {
final List<SqmPath<?>> items = new ArrayList<>();
for ( Order<? super R> key : keyDefinition ) {
if ( !key.getEntityClass().isAssignableFrom( selected.getJavaType() ) ) {
throw new IllegalQueryOperationException("Select item was of wrong entity type");
if ( key.getEntityClass() == null ) {
throw new IllegalQueryOperationException("Key-based pagination based on select list items is not yet supported");
}
else {
if ( !key.getEntityClass().isAssignableFrom( selected.getJavaType() ) ) {
throw new IllegalQueryOperationException("Select item was of wrong entity type");
}
// ordering by an attribute of the returned entity
items.add( root.get( key.getAttributeName() ) );
}
// ordering by an attribute of the returned entity
items.add( root.get( key.getAttributeName() ) );
}
return keyedResultConstructor( selected, builder, items );
}

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.query.sqm.internal;
import java.util.ArrayList;
import java.util.List;
/**
@ -30,4 +31,26 @@ class KeyedResult<R> {
public List<Comparable<?>> getKey() {
return key;
}
static <R> List<R> collectResults(List<KeyedResult<R>> executed, int pageSize) {
final int size = executed.size();
final List<R> resultList = new ArrayList<>( size );
for (int i = 0; i < size && i < pageSize; i++) {
resultList.add( executed.get(i).getResult() );
}
return resultList;
}
static List<List<?>> collectKeys(List<? extends KeyedResult<?>> executed, int pageSize) {
final int size = executed.size();
final List<List<?>> resultList = new ArrayList<>( size );
for ( int i = 0; i < size && i < pageSize; i++ ) {
final List<Comparable<?>> key = executed.get(i).getKey();
if ( key == null ) {
throw new IllegalArgumentException("Null keys in key-based pagination are not yet supported");
}
resultList.add( key );
}
return resultList;
}
}