more javadoc for pagination APIs

This commit is contained in:
Gavin King 2024-03-09 20:31:56 +01:00
parent ef16d42c65
commit 30b8cdeb84
3 changed files with 96 additions and 0 deletions

View File

@ -20,6 +20,21 @@ 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}.
* <p>
* In this context, a <em>key</em> is a unique key of the
* query result set which imposes a total order on the results.
* It is represented as a {@code List<Order<? super R>>} 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:
* <pre>
* var key = List.of(asc(Book_.title), asc(Book_.publicationDate), asc(Book_.publisher));
* </pre>
* <p>
* 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.
* <p>
* A specification for an initial page may be obtained from
* an instance of {@link Page}.
* <pre>

View File

@ -16,6 +16,38 @@ import java.util.List;
* An instance of this class represent a page of results returned
* by {@link SelectionQuery#getKeyedResultList(KeyedPage)}. The
* actual query results are held in {@link #getResultList()}.
* <p>
* An idiom for iterating pages of keyed results is:
* <pre>
* var query =
* session.createQuery("where title like :title", Book.class)
* .setParameter("title", "%Hibernate%");
* var resultList =
* query.getKeyedResultList(first(10).keyedBy(asc(Book_.isbn)));
* var results = resultList.getResultList();
* ...
* while ( !resultList.isLastPage() ) {
* resultList = query.getKeyedResultList(resultList.getNextPage());
* results = resultList.getResultList();
* ...
* }
* </pre>
* <p>
* When {@code KeyedResultList} is the declared return type of a
* {@linkplain org.hibernate.annotations.processing.Find finder
* method} or {@linkplain org.hibernate.annotations.processing.HQL
* HQL query method}, the idiom may be written:
* <pre>
* var resultList =
* books.byTitle("%Hibernate%", first(10).keyedBy(asc(Book_.isbn)));
* var results = resultList.getResultList();
* ...
* while ( !resultList.isLastPage() ) {
* resultList = books.byTitle("%Hibernate%", resultList.getNextPage());
* results = resultList.getResultList();
* ...
* }
* </pre>
*
* @since 6.5
*

View File

@ -21,6 +21,9 @@ import java.util.List;
* 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}.
* <p>
* For key-based pagination, call {@link #keyedBy(Order)} to obtain a
* {@link KeyedPage}.
*
* @see SelectionQuery#setPage(Page)
*
@ -33,22 +36,41 @@ public class Page {
private final int size;
private final int number;
/**
* The size of the page, that is, the maximum number
* of results on the page.
*/
public int getSize() {
return size;
}
/**
* The page number, where {@code 0} is the first page.
*/
public int getNumber() {
return number;
}
/**
* @return {@code true} is this is the first page.
*/
public boolean isFirst() {
return number == 0;
}
/**
* @return the page {@linkplain #size}, that is, the
* {@linkplain SelectionQuery#getMaxResults()
* maximum number of results} on the page.
*/
public int getMaxResults() {
return size;
}
/**
* @return the {@linkplain SelectionQuery#getFirstResult()
* offset} of this page.
*/
public int getFirstResult() {
return size*number;
}
@ -64,18 +86,34 @@ public class Page {
this.number = number;
}
/**
* Obtain a page, numbered from {@code 0}, with the given size.
* @param size the number of results on the initial page.
* @param number the number of the page, where {@code 0} is the first page.
*/
public static Page page(int size, int number) {
return new Page( size, number );
}
/**
* Obtain an initial page with the given size.
* @param size the number of results on the initial page.
*/
public static Page first(int size) {
return new Page( size, 0 );
}
/**
* Obtain the next page with the same size as this page.
*/
public Page next() {
return new Page( size, number+1 );
}
/**
* Obtain the previous page with the same size as this page.
* @throws IllegalStateException if this is the first page
*/
public Page previous() {
if ( isFirst() ) {
throw new IllegalStateException("already at first page");
@ -87,6 +125,15 @@ public class Page {
return first( size );
}
/**
* Obtain a {@linkplain KeyedPage key-based specification} of this page.
*
* @param keyDefinition an {@link Order} object defining a total order
* on the result set
* @return a {@link KeyedPage} representing this page
*
* @since 6.5
*/
public <R> KeyedPage<R> keyedBy(Order<? super R> keyDefinition) {
if ( keyDefinition == null ) {
throw new IllegalArgumentException("Key definition must not null");
@ -100,6 +147,8 @@ public class Page {
* @param keyDefinition a list of {@link Order} objects defining a total
* order on the result set
* @return a {@link KeyedPage} representing this page
*
* @since 6.5
*/
public <R> KeyedPage<R> keyedBy(List<Order<? super R>> keyDefinition) {
if ( keyDefinition == null || keyDefinition.isEmpty() ) {