Skip to content

Commit

Permalink
simplify pagination API
Browse files Browse the repository at this point in the history
implements the solution suggested by @njr-11

see jakartaee#504
  • Loading branch information
gavinking committed Mar 3, 2024
1 parent bda793e commit 2414a21
Show file tree
Hide file tree
Showing 26 changed files with 406 additions and 624 deletions.
47 changes: 0 additions & 47 deletions api/src/main/java/jakarta/data/Streamable.java

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,14 @@
* because these become non-matching.</p>
*
* <p>To use keyset pagination, define a repository method with return value of
* {@link KeysetAwareSlice} or {@link KeysetAwarePage} and which accepts a
* special parameter (after the normal query parameters) that is a
* {@link PageRequest}. For example,</p>
* {@link CursoredPage} and which accepts a special parameter (after the normal
* query parameters) that is a {@link PageRequest}. For example,</p>
*
* <pre>
* &#64;OrderBy("lastName")
* &#64;OrderBy("firstName")
* &#64;OrderBy("id")
* KeysetAwareSlice&lt;Employee&gt; findByHoursWorkedGreaterThan(int hours, {@code PageRequest<Employee>} pageRequest);
* CursoredPage&lt;Employee&gt; findByHoursWorkedGreaterThan(int hours, {@code PageRequest<Employee>} pageRequest);
* </pre>
*
* <p>You can use an offset-based {@link PageRequest} to request an initial page,</p>
Expand All @@ -66,7 +65,7 @@
* </pre>
*
* <p>Because the page is keyset aware, the {@link PageRequest}
* that it returns from the call to {@link KeysetAwareSlice#nextPageRequest}
* that it returns from the call to {@link CursoredPage#nextPageRequest}
* above is based upon a keyset cursor from that page to use as a starting point
* after which the results for the next page are to be found.</p>
*
Expand Down Expand Up @@ -114,7 +113,7 @@
* &#64;OrderBy("zipcode")
* &#64;OrderBy("birthYear")
* &#64;OrderBy("id")
* KeysetAwareSlice&lt;Customer&gt; getTopBuyers(int minOrders, float minSpent,
* CursoredPage&lt;Customer&gt; getTopBuyers(int minOrders, float minSpent,
* {@code PageRequest<Customer>} pageRequest);
* </pre>
*
Expand All @@ -130,14 +129,14 @@
*
* <h2>Database Support for Keyset Pagination</h2>
*
* <p>A repository method with return type of <code>KeysetAwareSlice</code> or
* {@link KeysetAwarePage} must raise {@link UnsupportedOperationException}
* if the database is incapable of keyset pagination.
* <p>A repository method with return type of <code>CursoredPage</code> must
* raise {@link UnsupportedOperationException} if the database is incapable
* of keyset pagination.
* </p>
*
* @param <T> the type of elements in this slice
*/
public interface KeysetAwareSlice<T> extends Slice<T> {
public interface CursoredPage<T> extends Page<T> {
/**
* Returns a {@link PageRequest.Cursor Cursor} for keyset values at the
* specified position.
Expand Down
35 changes: 0 additions & 35 deletions api/src/main/java/jakarta/data/page/KeysetAwarePage.java

This file was deleted.

149 changes: 129 additions & 20 deletions api/src/main/java/jakarta/data/page/Page.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023,2024 Contributors to the Eclipse Foundation
* Copyright (c) 2022,2024 Contributors to the Eclipse Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,39 +17,148 @@
*/
package jakarta.data.page;

import jakarta.data.repository.Query;
import java.util.List;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
* <p>A page contains the data that is retrieved for a page request
* and has awareness of the total number of pages.</p>
*
* <p>A page is a sublist of results. It provides information about its position relative to the entire list.
* A page is obtained by supplying a {@link PageRequest} parameter to a repository method. For example,</p>
* <p>A page contains the data that is retrieved to satisfy a given page request.
* An instance of {@code Page} is obtained by supplying a {@link PageRequest} as
* an argument of a repository method. For example,</p>
*
* <pre>
* {@code Page<Employee>} findByYearHired(int year, {@code PageRequest<?>} pageRequest);
* {@code @Find}
* {@code Page<Vehicle>} search({@code @By("make")} String make,
* {@code @By("model")} String model,
* {@code @By("year")} int designYear,
* {@code PageRequest<?>} pageRequest);
* </pre>
*
* <p>Repository methods that are declared to return <code>Page</code> or
* {@link KeysetAwarePage} must raise {@link UnsupportedOperationException} if the
* database is incapable of counting the total number of results across all pages,
* in which case a return type of {@link Slice} or {@link KeysetAwareSlice}
* should be used instead.</p>
*
* <p>For a lighter weight subset of query results that does not have awareness of the
* total number of pages, {@link Slice} can be used instead of page.</p>
* <p>If {@link PageRequest#requestTotal()} is enabled, the {@link Page} also
* contains information about the {@linkplain #totalPages total number of pages}
* and the {@linkplain #totalElements total number of elements} that can be
* retrieved by the query.</p>
*
* @param <T> the type of elements in this page
* @param <T> the type of elements in this page.
*/
public interface Page<T> extends Slice<T> {
public interface Page<T> extends Iterable<T> {

/**
* Returns the page content as a {@link List}.
* The list is sorted according to the combined sort criteria of the repository method
* and the sort criteria of the page request that is supplied to the repository method,
* sorting first by the sort criteria of the repository method,
* and then by the sort criteria of the page request.
*
* @return the page content as a {@link List}; will never be {@code null}.
*/
List<T> content();

/**
* Returns whether the {@link Page} has content at all.
*
* @return whether the {@link Page} has content at all.
*/
boolean hasContent();

/**
* Returns a sequential stream of results, which follow the order of the sort criteria if specified.
*
* @return a stream of results.
*/
default Stream<T> stream() {
return StreamSupport.stream(spliterator(), false);
}

/**
* Returns the number of elements on this {@code Page},
* which must be no larger than the maximum
* {@link PageRequest#size() size} of the page request.
* If the number of elements in the slice is less than the maximum page size,
* then there are no subsequent slices of data to read.
*
* @return the number of elements on this {@code Page}.
*/
int numberOfElements();

/**
* Returns {@code true} if it is known that there are more results or that it is
* necessary to request a next page to determine whether there are more results, so that
* {@link #nextPageRequest()} will definitely not return {@code null}.
* @return {@code false} if this is the last page of results.
*/
boolean hasNext();

/**
* Returns the {@link PageRequest page request} for which this
* slice was obtained.
*
* @return the request for the current page; will never be {@code null}.
*/
PageRequest<T> pageRequest();

/**
* <p>Returns the {@link PageRequest page request} for which this
* slice was obtained.</p>
*
* <p>This method is provided for when {@link Query query language} is used to
* return a result of different type than the entity that is being queried.
* This method allows the {@link PageRequest} to be returned for the
* type of entity class that was queried.</p>
*
* @param <E> entity class of the attributes that are used as sort criteria.
* @param entityClass entity class of the attributes that are used as sort criteria.
* @return the request for the current page; will never be {@code null}.
*/
<E> PageRequest<E> pageRequest(Class<E> entityClass);

/**
* Returns a request for the {@link PageRequest#next() next} page, or <code>null</code> if it is known that there is no next page.
*
* @return a request for the next page.
*/
PageRequest<T> nextPageRequest();

/**
* <p>Returns a request for the {@link PageRequest#next() next} page,
* or <code>null</code> if it is known that there is no next page.</p>
*
* <p>This method is useful when {@link Query query language} is used to
* return a result of different type than the entity that is being queried.
* This method allows the subsequent {@link PageRequest} to be returned for the
* type of entity class that is being queried.</p>
*
* @param <E> entity class of the attributes that are used as sort criteria.
* @param entityClass entity class of the attributes that are used as sort criteria.
* @return a request for the next page.
*/
<E> PageRequest<E> nextPageRequest(Class<E> entityClass);

/**
* Returns {@code true} if the {@link #pageRequest()} specified that the
* {@linkplain PageRequest#requestTotal total number of elements should
* be retrieved from the database}, and that it is therefore safe to call
* {@link #totalElements()} or {@link #totalPages()}.
* @return {@code true} if totals are available.
*/
boolean hasTotals();

/**
* Returns the total number of elements across all pages that can be requested for the query.
* Returns the total number of elements across all pages of query results, if the
* {@link #pageRequest()} specified that {@linkplain PageRequest#requestTotal the
* total should be retrieved from the database}.
* @return the total number of elements across all pages.
* @throws IllegalStateException if the total was not retrieved from the database.
*/
long totalElements();

/**
* Returns the total number of pages.
* Returns the total number of pages of query results, if the {@link #pageRequest()}
* specified that {@linkplain PageRequest#requestTotal the total should be retrieved
* from the database}.
* @return the total number of pages.
* @throws IllegalStateException if the total was not retrieved from the database.
*/
long totalPages();
}
}
Loading

0 comments on commit 2414a21

Please sign in to comment.