org.hibernate.query.KeyedPage Maven / Gradle / Ivy
Show all versions of beangle-hibernate-core Show documentation
/*
* 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 org.hibernate.Incubating;
import org.hibernate.Internal;
import java.util.List;
import static java.util.Collections.unmodifiableList;
import static org.hibernate.query.KeyedPage.KeyInterpretation.KEY_OF_FIRST_ON_NEXT_PAGE;
import static org.hibernate.query.KeyedPage.KeyInterpretation.KEY_OF_LAST_ON_PREVIOUS_PAGE;
import static org.hibernate.query.KeyedPage.KeyInterpretation.NO_KEY;
/**
* Support for pagination based on a unique key of the result
* set instead of the {@link Page#getFirstResult() offset}.
*
* In this context, a key is a unique key of the
* query result set which imposes a total order on the results.
* It is represented as a {@code List>} where
* {@code R} is the result type of the query. For example, a
* unique key for paginating a query result set containing
* {@code Book}s might be:
*
* var key = List.of(asc(Book_.title), asc(Book_.publicationDate), asc(Book_.publisher));
*
*
* When key-based pagination is used, Hibernate modifies the
* original HQL or criteria query to incorporate the key in
* the {@code order by}, {@code where}, and {@code select}
* clauses.
*
* A specification for an initial page may be obtained from
* an instance of {@link Page}.
*
* KeyedPage<Book> firstPage = Page.first(10).keyedBy(asc(Book_.isbn)));
*
* A {@link KeyedResultList} then may be obtained by calling
* {@link SelectionQuery#getKeyedResultList(KeyedPage)}.
*
* KeyedResultList results =
* session.createQuery("from Book", Book.class)
* .getKeyedResultList(firstPage);
*
* The following page may be obtained from {@link KeyedResultList#getNextPage()}.
*
* KeyedPage<Book> nextPage = results.getNextPage();
* KeyedResultList moreResults =
* session.createQuery("from Book", Book.class)
* .getKeyedResultList(nextPage);
*
*
* A parameter of a {@linkplain org.hibernate.annotations.processing.Find
* finder method} or {@linkplain org.hibernate.annotations.processing.HQL
* HQL query method} may be declared with type {@code Page}. Then the
* return type of the method should be {@link KeyedResultList}.
*
* @since 6.5
*
* @see SelectionQuery#getKeyedResultList(KeyedPage)
* @see KeyedResultList
*
* @author Gavin King
*/
@Incubating
public class KeyedPage {
private final List> keyDefinition;
private final Page page;
private final List> key;
private final KeyInterpretation keyInterpretation;
KeyedPage(List> keyDefinition, Page page) {
this( keyDefinition, page, null, NO_KEY );
}
KeyedPage(List> keyDefinition, Page page, List> key, KeyInterpretation interpretation) {
this.keyDefinition = unmodifiableList(keyDefinition);
this.page = page;
this.key = key;
this.keyInterpretation = interpretation;
}
/**
* A key definition for key-based pagination. The list of {@link Order}
* objects must define a total ordering of the query result set, and
* thus forms a unique key on the result set.
*/
public List> getKeyDefinition() {
return keyDefinition;
}
/**
* A specification of this page in terms of page size and an
* (approximate) page number.
*/
public Page getPage() {
return page;
}
/**
* The key of the last result on the previous page, or of the
* first result on the next page, which may be used to locate
* the start or end, respectively, of the current page.
*
* A null key indicates that an {@linkplain Page#getFirstResult()
* offset} should be used instead. This is used to obtain an
* initial page of results.
*
* @return the key, or null if an offset should be used
*/
public List> getKey() {
return key;
}
/**
* Determines whether the {@link #getKey() key} should be
* interpreted as the last result on the previous page, or
* as the first result on the next page.
*/
public KeyInterpretation getKeyInterpretation() {
return keyInterpretation;
}
/**
* Obtain a specification of the next page of results, which is
* to be located using the given key, which must be the key of
* the last result on this page.
*
* @param keyOfLastResultOnThisPage the key of the last result on this page
* @return a {@link KeyedPage} representing the next page of results
*/
@Internal
public KeyedPage nextPage(List> keyOfLastResultOnThisPage) {
return new KeyedPage<>( keyDefinition, page.next(), keyOfLastResultOnThisPage, KEY_OF_LAST_ON_PREVIOUS_PAGE );
}
/**
* Obtain a specification of the previous page of results, which
* is to be located using the given key, which must be the key of
* the first result on this page.
*
* @param keyOfFirstResultOnThisPage the key of the first result on this page
* @return a {@link KeyedPage} representing the next page of results
*/
@Internal
public KeyedPage previousPage(List> keyOfFirstResultOnThisPage) {
if ( page.isFirst() ) {
return null;
}
else {
return new KeyedPage<>( keyDefinition, page.previous(), keyOfFirstResultOnThisPage, KEY_OF_FIRST_ON_NEXT_PAGE );
}
}
/**
* Attach the given key to the specification of this page,
* with the given interpretation.
*
* @return a {@link KeyedPage} representing the same page
* of results, but which may be located using the
* given key
*/
@Internal
public KeyedPage withKey(List> key, KeyInterpretation interpretation) {
return new KeyedPage<>( keyDefinition, page, key, interpretation );
}
public enum KeyInterpretation {
KEY_OF_LAST_ON_PREVIOUS_PAGE,
KEY_OF_FIRST_ON_NEXT_PAGE,
NO_KEY
}
}