HHH-16815 more convenient pagination via Query API

- add Page convenience class
- add Query.paginate(int, int)
- add Query.paginate(Page)
This commit is contained in:
Gavin King 2023-06-28 14:17:44 +02:00
parent f7e12d49ed
commit 1e46146b54
8 changed files with 201 additions and 84 deletions

View File

@ -900,7 +900,7 @@ Unfortunately, there's no way to do this using JPA's `TypedQuery` interface.
| `setFirstResult()` | Set an offset on the results returned by a query | ✔
| `ascending()` | Add a field to use to order the results | ✖
| `descending()` | Add a field to use to order the results | ✖
| `unordered()` | Clear any current ordering | ✖
| `clearOrder()` | Clear any current ordering | ✖
|===
[[projection-lists]]

View File

@ -0,0 +1,63 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.query;
/**
* Identifies a page of query results by {@linkplain #size page size}
* and {@linkplain #number page number}.
* <p>
* This is a convenience class which allows a reference to a page of
* results to be passed around the system before being applied to
* a {@link Query} by calling {@link Query#paginate(Page)}.
*
* @see Query#paginate(Page)
*
* @since 6.3
*
* @author Gavin King
*/
public class Page {
private final int size;
private final int number;
public int getSize() {
return size;
}
public int getNumber() {
return number;
}
public int getMaxResults() {
return size;
}
public int getFirstResult() {
return size*number;
}
public Page(int size, int number) {
this.size = size;
this.number = number;
}
public static Page first(int size) {
return new Page(0, size);
}
public Page next() {
return new Page( size, number+1 );
}
public Page previous() {
return new Page( size, number+1 );
}
public Page first() {
return first( size );
}
}

View File

@ -290,7 +290,6 @@ public interface Query<R> extends SelectionQuery<R>, MutationQuery, TypedQuery<R
* @apiNote This method calls {@link #applyGraph(RootGraph, GraphSemantic)}
* using {@link GraphSemantic#LOAD} as the semantic.
*/
@SuppressWarnings("UnusedDeclaration")
default Query<R> applyLoadGraph(@SuppressWarnings("rawtypes") RootGraph graph) {
return applyGraph( graph, GraphSemantic.LOAD );
}
@ -890,6 +889,20 @@ public interface Query<R> extends SelectionQuery<R>, MutationQuery, TypedQuery<R
@Override
Query<R> setFirstResult(int startPosition);
@Override
default Query<R> paginate(int pageSize, int pageNumber) {
setFirstResult( pageNumber * pageSize );
setMaxResults( pageSize );
return this;
}
@Override
default Query<R> paginate(Page page) {
setMaxResults( page.getMaxResults() );
setFirstResult( page.getFirstResult() );
return this;
}
@Override
Query<R> setHint(String hintName, Object value);

View File

@ -316,16 +316,37 @@ public interface SelectionQuery<R> extends CommonQueryContract {
/**
* The first row position to return from the query results. Applied
* to the SQL query
* to the SQL query.
*/
int getFirstResult();
/**
* Set the first row position to return from the query results. Applied
* to the SQL query
* to the SQL query.
*/
SelectionQuery<R> setFirstResult(int startPosition);
/**
* Set the page of results to return.
*
* @param pageNumber the page to return, where pages are numbered from zero
* @param pageSize the number of results per page
*
* @since 6.3
*/
@Incubating
SelectionQuery<R> paginate(int pageSize, int pageNumber);
/**
* Set the {@linkplain Page page} of results to return.
*
* @see Page
*
* @since 6.3
*/
@Incubating
SelectionQuery<R> paginate(Page page);
/**
* Obtain the {@link CacheMode} in effect for this query. By default,
* the query inherits the {@link CacheMode} of the session from which
@ -510,6 +531,8 @@ public interface SelectionQuery<R> extends CommonQueryContract {
*
* @param sorts one or more instances of {@link Sort}
*
* @see Sort
*
* @since 6.3
*/
@Incubating

View File

@ -1,3 +1,9 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.query;
import jakarta.persistence.metamodel.SingularAttribute;

View File

@ -118,7 +118,6 @@ public abstract class AbstractQuery<R>
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// QueryOptions handling
@Override
public QueryImplementor<R> setHint(String hintName, Object value) {
super.setHint( hintName, value );

View File

@ -618,13 +618,10 @@ public abstract class AbstractSelectionQuery<R>
@Override
public SelectionQuery<R> setFirstResult(int startPosition) {
getSession().checkOpen();
if ( startPosition < 0 ) {
throw new IllegalArgumentException( "first-result value cannot be negative : " + startPosition );
}
getQueryOptions().getLimit().setFirstRow( startPosition );
return this;
}

View File

@ -35,7 +35,7 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.graph.spi.AppliedGraph;
import org.hibernate.internal.util.collections.IdentitySet;
import org.hibernate.query.BindableType;
import org.hibernate.query.Query;
import org.hibernate.query.Page;
import org.hibernate.query.QueryLogging;
import org.hibernate.query.QueryParameter;
import org.hibernate.query.SelectionQuery;
@ -279,6 +279,37 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R>
return hql;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// convenience methods
@Override
public SelectionQuery<R> paginate(int pageSize, int pageNumber) {
setFirstResult( pageNumber * pageSize );
setMaxResults( pageSize );
return this;
}
@Override
public SelectionQuery<R> paginate(Page page) {
setMaxResults( page.getMaxResults() );
setFirstResult( page.getFirstResult() );
return this;
}
@Override @SafeVarargs
public final SelectionQuery<R> sort(Sort<? super R>... sorts) {
for (Sort<? super R> sort: sorts) {
SingularAttribute<? super R,?> attribute = sort.getAttribute();
if ( attribute == null ) {
attribute =
getSession().getFactory().getMetamodel()
.entity( sort.getEntityClass() )
.getSingularAttribute( sort.getAttributeName() );
}
sort( sort.getOrder(), attribute );
}
return this;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// execution
@ -578,21 +609,6 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R>
return null;
}
@Override @SafeVarargs
public final SqmSelectionQuery<R> sort(Sort<? super R>... sorts) {
for (Sort<? super R> sort: sorts) {
SingularAttribute<? super R,?> attribute = sort.getAttribute();
if ( attribute == null ) {
attribute =
getSession().getFactory().getMetamodel()
.entity( sort.getEntityClass() )
.getSingularAttribute( sort.getAttributeName() );
}
sort( sort.getOrder(), attribute );
}
return this;
}
@Override
public SqmSelectionQuery<R> ascending(int element) {
addOrdering( element, SortOrder.ASCENDING );