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:
parent
f7e12d49ed
commit
1e46146b54
|
@ -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 | ✔
|
| `setFirstResult()` | Set an offset on the results returned by a query | ✔
|
||||||
| `ascending()` | Add a field to use to order the results | ✖
|
| `ascending()` | Add a field to use to order the results | ✖
|
||||||
| `descending()` | 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]]
|
[[projection-lists]]
|
||||||
|
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -290,7 +290,6 @@ public interface Query<R> extends SelectionQuery<R>, MutationQuery, TypedQuery<R
|
||||||
* @apiNote This method calls {@link #applyGraph(RootGraph, GraphSemantic)}
|
* @apiNote This method calls {@link #applyGraph(RootGraph, GraphSemantic)}
|
||||||
* using {@link GraphSemantic#LOAD} as the semantic.
|
* using {@link GraphSemantic#LOAD} as the semantic.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
|
||||||
default Query<R> applyLoadGraph(@SuppressWarnings("rawtypes") RootGraph graph) {
|
default Query<R> applyLoadGraph(@SuppressWarnings("rawtypes") RootGraph graph) {
|
||||||
return applyGraph( graph, GraphSemantic.LOAD );
|
return applyGraph( graph, GraphSemantic.LOAD );
|
||||||
}
|
}
|
||||||
|
@ -890,6 +889,20 @@ public interface Query<R> extends SelectionQuery<R>, MutationQuery, TypedQuery<R
|
||||||
@Override
|
@Override
|
||||||
Query<R> setFirstResult(int startPosition);
|
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
|
@Override
|
||||||
Query<R> setHint(String hintName, Object value);
|
Query<R> setHint(String hintName, Object value);
|
||||||
|
|
||||||
|
|
|
@ -316,16 +316,37 @@ public interface SelectionQuery<R> extends CommonQueryContract {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The first row position to return from the query results. Applied
|
* The first row position to return from the query results. Applied
|
||||||
* to the SQL query
|
* to the SQL query.
|
||||||
*/
|
*/
|
||||||
int getFirstResult();
|
int getFirstResult();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the first row position to return from the query results. Applied
|
* 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);
|
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,
|
* Obtain the {@link CacheMode} in effect for this query. By default,
|
||||||
* the query inherits the {@link CacheMode} of the session from which
|
* 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}
|
* @param sorts one or more instances of {@link Sort}
|
||||||
*
|
*
|
||||||
|
* @see Sort
|
||||||
|
*
|
||||||
* @since 6.3
|
* @since 6.3
|
||||||
*/
|
*/
|
||||||
@Incubating
|
@Incubating
|
||||||
|
|
|
@ -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;
|
package org.hibernate.query;
|
||||||
|
|
||||||
import jakarta.persistence.metamodel.SingularAttribute;
|
import jakarta.persistence.metamodel.SingularAttribute;
|
||||||
|
@ -18,77 +24,77 @@ import jakarta.persistence.metamodel.SingularAttribute;
|
||||||
* @since 6.3
|
* @since 6.3
|
||||||
*/
|
*/
|
||||||
public class Sort<X> {
|
public class Sort<X> {
|
||||||
private final SortOrder order;
|
private final SortOrder order;
|
||||||
private final SingularAttribute<X,?> attribute;
|
private final SingularAttribute<X,?> attribute;
|
||||||
private final Class<X> entityClass;
|
private final Class<X> entityClass;
|
||||||
private final String attributeName;
|
private final String attributeName;
|
||||||
|
|
||||||
public Sort(SortOrder order, SingularAttribute<X, ?> attribute) {
|
public Sort(SortOrder order, SingularAttribute<X, ?> attribute) {
|
||||||
this.order = order;
|
this.order = order;
|
||||||
this.attribute = attribute;
|
this.attribute = attribute;
|
||||||
this.attributeName = attribute.getName();
|
this.attributeName = attribute.getName();
|
||||||
this.entityClass = attribute.getDeclaringType().getJavaType();
|
this.entityClass = attribute.getDeclaringType().getJavaType();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Sort(SortOrder order, Class<X> entityClass, String attributeName) {
|
public Sort(SortOrder order, Class<X> entityClass, String attributeName) {
|
||||||
this.order = order;
|
this.order = order;
|
||||||
this.entityClass = entityClass;
|
this.entityClass = entityClass;
|
||||||
this.attributeName = attributeName;
|
this.attributeName = attributeName;
|
||||||
this.attribute = null;
|
this.attribute = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> Sort<T> asc(SingularAttribute<T,?> attribute) {
|
public static <T> Sort<T> asc(SingularAttribute<T,?> attribute) {
|
||||||
return new Sort<>(SortOrder.ASCENDING, attribute);
|
return new Sort<>(SortOrder.ASCENDING, attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> Sort<T> desc(SingularAttribute<T,?> attribute) {
|
public static <T> Sort<T> desc(SingularAttribute<T,?> attribute) {
|
||||||
return new Sort<>(SortOrder.ASCENDING, attribute);
|
return new Sort<>(SortOrder.ASCENDING, attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> Sort<T> asc(Class<T> entityClass, String attributeName) {
|
public static <T> Sort<T> asc(Class<T> entityClass, String attributeName) {
|
||||||
return new Sort<>( SortOrder.ASCENDING, entityClass, attributeName );
|
return new Sort<>( SortOrder.ASCENDING, entityClass, attributeName );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> Sort<T> desc(Class<T> entityClass, String attributeName) {
|
public static <T> Sort<T> desc(Class<T> entityClass, String attributeName) {
|
||||||
return new Sort<>( SortOrder.ASCENDING, entityClass, attributeName );
|
return new Sort<>( SortOrder.ASCENDING, entityClass, attributeName );
|
||||||
}
|
}
|
||||||
|
|
||||||
public SortOrder getOrder() {
|
public SortOrder getOrder() {
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SingularAttribute<X, ?> getAttribute() {
|
public SingularAttribute<X, ?> getAttribute() {
|
||||||
return attribute;
|
return attribute;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Class<X> getEntityClass() {
|
public Class<X> getEntityClass() {
|
||||||
return entityClass;
|
return entityClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAttributeName() {
|
public String getAttributeName() {
|
||||||
return attributeName;
|
return attributeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return attributeName + " " + order;
|
return attributeName + " " + order;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if ( o instanceof Sort ) {
|
if ( o instanceof Sort ) {
|
||||||
Sort<?> that = (Sort<?>) o;
|
Sort<?> that = (Sort<?>) o;
|
||||||
return that.order == order
|
return that.order == order
|
||||||
&& that.attributeName.equals(attributeName)
|
&& that.attributeName.equals(attributeName)
|
||||||
&& that.entityClass.equals(entityClass);
|
&& that.entityClass.equals(entityClass);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return attributeName.hashCode() + entityClass.hashCode();
|
return attributeName.hashCode() + entityClass.hashCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,7 +118,6 @@ public abstract class AbstractQuery<R>
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// QueryOptions handling
|
// QueryOptions handling
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public QueryImplementor<R> setHint(String hintName, Object value) {
|
public QueryImplementor<R> setHint(String hintName, Object value) {
|
||||||
super.setHint( hintName, value );
|
super.setHint( hintName, value );
|
||||||
|
|
|
@ -618,13 +618,10 @@ public abstract class AbstractSelectionQuery<R>
|
||||||
@Override
|
@Override
|
||||||
public SelectionQuery<R> setFirstResult(int startPosition) {
|
public SelectionQuery<R> setFirstResult(int startPosition) {
|
||||||
getSession().checkOpen();
|
getSession().checkOpen();
|
||||||
|
|
||||||
if ( startPosition < 0 ) {
|
if ( startPosition < 0 ) {
|
||||||
throw new IllegalArgumentException( "first-result value cannot be negative : " + startPosition );
|
throw new IllegalArgumentException( "first-result value cannot be negative : " + startPosition );
|
||||||
}
|
}
|
||||||
|
|
||||||
getQueryOptions().getLimit().setFirstRow( startPosition );
|
getQueryOptions().getLimit().setFirstRow( startPosition );
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.graph.spi.AppliedGraph;
|
import org.hibernate.graph.spi.AppliedGraph;
|
||||||
import org.hibernate.internal.util.collections.IdentitySet;
|
import org.hibernate.internal.util.collections.IdentitySet;
|
||||||
import org.hibernate.query.BindableType;
|
import org.hibernate.query.BindableType;
|
||||||
import org.hibernate.query.Query;
|
import org.hibernate.query.Page;
|
||||||
import org.hibernate.query.QueryLogging;
|
import org.hibernate.query.QueryLogging;
|
||||||
import org.hibernate.query.QueryParameter;
|
import org.hibernate.query.QueryParameter;
|
||||||
import org.hibernate.query.SelectionQuery;
|
import org.hibernate.query.SelectionQuery;
|
||||||
|
@ -279,6 +279,37 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R>
|
||||||
return hql;
|
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
|
// execution
|
||||||
|
@ -578,21 +609,6 @@ public class SqmSelectionQueryImpl<R> extends AbstractSelectionQuery<R>
|
||||||
return null;
|
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
|
@Override
|
||||||
public SqmSelectionQuery<R> ascending(int element) {
|
public SqmSelectionQuery<R> ascending(int element) {
|
||||||
addOrdering( element, SortOrder.ASCENDING );
|
addOrdering( element, SortOrder.ASCENDING );
|
||||||
|
|
Loading…
Reference in New Issue