Interface KeysetAwareSlice<T>
- Type Parameters:
T
- the type of elements in this slice
- All Superinterfaces:
Iterable<T>
,Slice<T>
,Streamable<T>
- All Known Subinterfaces:
KeysetAwarePage<T>
Keyset pagination is a form of pagination that aims to reduce the possibility of missed or duplicate results by making the request for each subsequent page relative to the observed values of entity properties from the current page. This list of values is referred to as the keyset and only includes values of entity properties that are in the sort criteria of the repository method. The combination of sort criteria must uniquely identify each entity. The keyset values can be from the last entity (for pagination in a forward direction) or first entity on the page (if requesting pages in a reverse direction), or can be any other desired list of values which serve as a new starting point. Keyset pagination also has the potential to improve performance by avoiding the fetching and ordering of results from prior pages because these become non-matching.
To use keyset pagination, define a repository method with return value of
KeysetAwareSlice
or KeysetAwarePage
and which accepts a
special parameter (after the normal query parameters) that is a
Pageable
. For example,
@OrderBy("lastName") @OrderBy("firstName") @OrderBy("id") KeysetAwareSlice<Employee> findByHoursWorkedGreaterThan(int hours, Pageable pagination);
You can use a normal Pageable
to request an initial page,
page = employees.findByHoursWorkedGreaterThan(1500, Pageable.ofSize(50));
For subsequent pages, you can request pagination relative to the end of the current page as follows,
page = employees.findByHoursWorkedGreaterThan(1500, page.nextPageable());
Because the page is keyset aware, the Pageable
that it returns from the call to nextPageable()
above is based upon a keyset from that page to use as a starting point
after which the results for the next page are to be found.
You can also construct a Pageable
with a Cursor
directly, which
allows you to make it relative to a specific list of values. The number and
order of values must match that of the OrderBy
annotations,
Pageable.sortBy(Sort...)
or Pageable.sortBy(Iterable)
parameters,
or OrderBy
name pattern of the repository method.
For example,
Employee emp = ... Pageable pagination = Pageable.ofSize(50).afterKeyset(emp.lastName, emp.firstName, emp.id); page = employees.findByHoursWorkedGreaterThan(1500, pagination);
By making the query for the next page relative to observed values, not a numerical position, keyset pagination is less vulnerable to changes that are made to data in between page requests. Adding or removing entities is possible without causing unexpected missed or duplicate results. Keyset pagination does not prevent misses and duplicates if the entity properties which are the sort criteria for existing entities are modified or if an entity is re-added with different sort criteria after having previously been removed.
Keyset Pagination with @Query
Keyset pagination involves generating and appending to the query
additional query conditions for the keyset properties. In order for that to
be possible, a user-provided
JPQL
query must end with a
WHERE
clause to which additional conditions can be appended.
Enclose the entire conditional expression of the WHERE
clause
in parenthesis.
Sort criteria must be specified independently from the user-provided query,
either with the OrderBy
annotation or
Pageable.sortBy(Sort...)
or Pageable.sortBy(Iterable)
parameters.
For example,
@Query("SELECT o FROM Customer o WHERE (o.ordersPlaced >= ?1 OR o.totalSpent >= ?2)") @OrderBy("zipcode") @OrderBy("birthYear") @OrderBy("id") KeysetAwareSlice<Customer> getTopBuyers(int minOrders, float minSpent, Pageable pagination);
Page Numbers and Totals
Page numbers, total numbers of elements across all pages, and total count of pages are not accurate when keyset pagination is used and should not be relied upon.
Database Support for Keyset Pagination
A repository method with return type of KeysetAwareSlice
or
KeysetAwarePage
must raise UnsupportedOperationException
if the database is incapable of keyset pagination.
-
Method Summary
Modifier and TypeMethodDescriptiongetKeysetCursor
(int index) Returns aCursor
for keyset values at the specified position.Returns pagination information for requesting the next page in a forward direction from the current page.Returns pagination information for requesting the previous page in a reverse direction from the current page.Methods inherited from interface java.lang.Iterable
forEach, iterator, spliterator
Methods inherited from interface jakarta.data.page.Slice
content, hasContent, numberOfElements, pageable
Methods inherited from interface jakarta.data.Streamable
stream
-
Method Details
-
getKeysetCursor
Returns aCursor
for keyset values at the specified position.- Parameters:
index
- position (0 is first) of a result on the page.- Returns:
- cursor for keyset values at the specified position.
-
nextPageable
Pageable nextPageable()Returns pagination information for requesting the next page in a forward direction from the current page. This method computes a keyset from the last entity of the current page and includes the keyset in the pagination information so that it can be used to obtain the next slice in a forward direction according to the sort criteria and relative to that entity.
- Specified by:
nextPageable
in interfaceSlice<T>
- Returns:
- pagination information for requesting the next page, or
null
if the current page is empty or if it is known that there is not a next page.
-
previousPageable
Pageable previousPageable()Returns pagination information for requesting the previous page in a reverse direction from the current page. This method computes a keyset from the first entity of the current page and includes the keyset in the pagination information so that it can be used to obtain the previous slice in a reverse direction to the sort criteria and relative to that entity. Within a single page, results are not reversed and remain ordered according to the sort criteria.
Page numbers are not accurate and should not be relied upon when using keyset pagination. Jakarta Data providers should aim to at least avoid returning negative or
0
as page numbers when traversing pages in the reverse direction (this might otherwise occur when matching entities are added prior to the first page and the previous page is requested) by assigning a page number of1
to such pages. This means that there can be multiple consecutive pages numbered1
and thatcurrentPage.previousPageable().next().page()
cannot be relied upon to return a page number that is equal to the current page number.- Returns:
- pagination information for requesting the previous page, or
null
if the current page is empty or if it is known that there is not a previous page.
-