Repository: jakartaee/data
Pull request #525 (2024-03-06 20:49:29)

migrate naming / terminology away from "keyset"
See #504.


WDYT, folks?


Patch:
diff --git a/api/src/main/java/jakarta/data/page/CursoredPage.java b/api/src/main/java/jakarta/data/page/CursoredPage.java
index 2a120e66..bca79cb3 100644
--- a/api/src/main/java/jakarta/data/page/CursoredPage.java
+++ b/api/src/main/java/jakarta/data/page/CursoredPage.java
@@ -22,27 +22,36 @@ import jakarta.data.Sort;
 import java.util.NoSuchElementException;
 
 /**
- * <p>A slice of data with the ability to create a cursor from the
- * keyset of each entity in the slice.</p>
- *
- * <p>Keyset cursor 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 cursor
- * and is an ordered list of the values of entity properties that are in the
- * combined sort criteria of the repository method, in the same order of precedence.
- * The combination of sort criteria must uniquely identify each entity.
- * The keyset values can be from the last entity on the page
- * (for the next page in a forward direction) or first entity on the page
- * (if requesting the previous page),
- * 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.</p>
- *
- * <p>To use keyset pagination, define a repository method with return value of
- * {@link CursoredPage} and which accepts a special parameter (after the normal
- * query parameters) that is a {@link PageRequest}. For example,</p>
+ * <p>A page of data retrieved to satisfy a given page request, with a
+ * {@linkplain PageRequest.Cursor cursor} for each result on the page.
+ * A repository method which returns the type {@code CursoredPage} uses
+ * <em>cursor-based pagination</em> to determine page boundaries.</p>
+ *
+ * <p>Compared to offset-based pagination, cursor-based pagination reduces
+ * the possibility of missed or duplicate results when records are
+ * inserted, deleted, or updated in the database between page requests.
+ * Cursor-based pagination is possible when a query result set has a
+ * well-defined total order, that is, when the results are sorted by a
+ * list of entity fields which forms a unique key on the result set.
+ * This list of entity fields must be the entity fields of the combined
+ * sort criteria of the repository method, in the same order of precedence.
+ * This could just be the identifier field of the entity, or it might be
+ * some other combination of fields which uniquely identifies each query
+ * result.</p>
+ *
+ * <p>When cursor-based pagination is used, a {@linkplain #nextPageRequest
+ * next page request} is made relative to the last entity of the current
+ * page and a {@linkplain #previousPageRequest previous page request} is
+ * made relative to the first entity of the current page. Alternatively,
+ * a page request might be made relative to an arbitrary starting point
+ * in the result set, that is, with an arbitrary value of the key.</p>
+ *
+ * <p>The key for a given element of the result set is represented by an
+ * instance of {@link jakarta.data.page.PageRequest.Cursor Cursor}.</p>
+ *
+ * <p>To use cursor-based pagination, declare a repository method with return
+ * type {@code CursoredPage} and with a special parameter (after the normal
+ * query parameters) of type {@link PageRequest}, for example:</p>
  *
  * <pre>
  * &#64;OrderBy("lastName")
@@ -51,62 +60,64 @@ import java.util.NoSuchElementException;
  * 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>
+ * <p>In initial page may be requested using an offset-based page request:</p>
  *
  * <pre>
  * page = employees.findByHoursWorkedGreaterThan(1500, PageRequest.of(Employee.class).size(50));
  * </pre>
  *
- * <p>For subsequent pages, you can request pagination relative to the
- * end of the current page as follows,</p>
+ * <p>The next page may be requested relative to the end of the current page,
+ * as follows:</p>
  *
  * <pre>
  * page = employees.findByHoursWorkedGreaterThan(1500, page.nextPageRequest());
  * </pre>
  *
- * <p>Because the page is keyset aware, the {@link PageRequest}
- * 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>
+ * <p>Here, the instance of {@link PageRequest} returned by
+ * {@link CursoredPage#nextPageRequest()} is based on a key value encapsulated
+ * in an instance of {@link PageRequest.Cursor Cursor} and identifying the last
+ * result on the current page.</p>
  *
- * <p>You can also construct a {@link PageRequest} with a {@link PageRequest.Cursor 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 {@link OrderBy} annotations,
- * {@link Sort} parameters of the page request,
- * or {@code OrderBy} name pattern of the repository method.
- * For example,</p>
+ * <p>A {@link PageRequest} based on an explicit key may be constructed by
+ * calling {@link PageRequest#afterKey(Object...)}. The arguments supplied
+ * to this method must match the list of sorting criteria specified by
+ * {@link OrderBy} annotations of the repository method, {@link Sort}
+ * parameters of the page request, or {@code OrderBy} name pattern of
+ * the repository method. For example:</p>
  *
  * <pre>
  * Employee emp = ...
  * {@code PageRequest<Employee>} pageRequest = PageRequest.of(Employee.class)
  *                                                .size(50)
- *                                                .afterKeyset(emp.lastName, emp.firstName, emp.id);
+ *                                                .afterKey(emp.lastName, emp.firstName, emp.id);
  * page = employees.findByHoursWorkedGreaterThan(1500, pageRequest);
  * </pre>
  *
  * <p>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.</p>
- *
- * <h2>Keyset Pagination with &#64;Query</h2>
- *
- * <p>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
- * <a href="https://eclipse-ee4j.github.io/jakartaee-tutorial/#full-query-language-syntax">JPQL</a>
- * query must end with a
- * {@code WHERE} clause to which additional conditions can be appended.
- * Enclose the entire conditional expression of the {@code WHERE} clause
- * in parenthesis.
- * Sort criteria must be specified independently from the user-provided query,
- * either with the {@link OrderBy} annotation or
- * {@link Sort} parameters to {@link PageRequest}.
- * For example,</p>
+ * instead of to a numerical position, cursor-based pagination is less
+ * vulnerable to changes made to data in between page requests. Adding or
+ * removing entities is possible without causing unexpected missed or
+ * duplicate results. Cursor-based 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.</p>
+ *
+ * <h2>Cursor-based Pagination with {@code @Query}</h2>
+ *
+ * <p>Cursor-based pagination involves generating and appending additional
+ * restrictions involving the key fields to the {@code WHERE} clause of the
+ * query. For this to be possible, a user-provided JDQL or JPQL query must
+ * end with a {@code WHERE} clause to which additional conditions may be
+ * appended without otherwise changing the semantics of the query:</p>
+ * <ul>
+ * <li>The entire conditional expression of the {@code WHERE} clause must
+ *     be enclosed in parentheses.
+ * <li>Sorting criteria must be specified independently of the user-provided
+ *     query, either via the {@link OrderBy} annotation or, or by passing
+ *     {@link Sort} criteria within the {@linkplain PageRequest#sorts() page
+ *     request}.
+ * </ul>
+ * <p>For example:</p>
  *
  * <pre>
  * &#64;Query("SELECT o FROM Customer o WHERE (o.ordersPlaced &gt;= ?1 OR o.totalSpent &gt;= ?2)")
@@ -117,34 +128,33 @@ import java.util.NoSuchElementException;
  *                                         {@code PageRequest<Customer>} pageRequest);
  * </pre>
  *
- * <p>Queries that are used with keyset pagination must return entities
- * because keyset cursors are created from the entity attribute values that
- * form the keyset.</p>
+ * <p>Only queries which return entities may be used with cursor-based pagination
+ * because cursors are created from the entity attribute values that
+ * form the unique key.</p>
  *
  * <h2>Page Numbers and Totals</h2>
  *
- * <p>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.</p>
+ * <p>Page numbers, total numbers of elements across all pages, and total count
+ * of pages are not accurate when cursor-based pagination is used and should not
+ * be relied upon.</p>
  *
- * <h2>Database Support for Keyset Pagination</h2>
+ * <h2>Database Support for Cursor-based Pagination</h2>
  *
- * <p>A repository method with return type of {@code CursoredPage} must
- * raise {@link UnsupportedOperationException} if the database is incapable
- * of keyset pagination.
- * </p>
+ * <p>A repository method with return type {@code CursoredPage} must raise
+ * {@link UnsupportedOperationException} if the database itself is not capable
+ * of cursor-based pagination.</p>
  *
  * @param <T> the type of elements in this slice 
  */
 public interface CursoredPage<T> extends Page<T> {
     /**
-     * Returns a {@link PageRequest.Cursor Cursor} for keyset values at the
+     * Returns a {@link PageRequest.Cursor Cursor} for key values at the
      * specified position.
      *
      * @param index position (0 is first) of a result on the page.
-     * @return cursor for keyset values at the specified position.
+     * @return cursor for key values at the specified position.
      */
-    PageRequest.Cursor getKeysetCursor(int index);
+    PageRequest.Cursor getCursor(int index);
 
     /**
      * Returns {@code true} when it is possible to navigate to a previous
@@ -157,7 +167,7 @@ public interface CursoredPage<T> extends Page<T> {
 
     /**
      * <p>Creates a request for the next page in a forward direction from
-     * the current page. This method computes a keyset cursor from the last
+     * the current page. This method computes a cursor from the last
      * entity of the current page and includes the cursor in the pagination
      * information so that it can be used to obtain the next page in a
      * forward direction according to the sort criteria and relative to that
@@ -173,17 +183,17 @@ public interface CursoredPage<T> extends Page<T> {
 
     /**
      * <p>Creates a request for the previous page in a reverse direction from
-     * the current page. This method computes a keyset cursor from the first
+     * the current page. This method computes a cursor from the first
      * entity of the current page and includes the cursor in the pagination
-     * information so that it can be used to obtain the previous slice in a
+     * information so that it can be used to obtain the previous page 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.</p>
      *
      * <p>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 {@code 0} as page numbers when
-     * traversing pages in the reverse direction (this might otherwise occur
+     * using cursor-based pagination. Jakarta Data providers should aim to at
+     * least avoid returning negative or {@code 0} as page numbers when
+     * traversing pages in the previous page 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 of {@code 1}
      * to such pages. This means that there can be multiple consecutive pages
diff --git a/api/src/main/java/jakarta/data/page/PageRequest.java b/api/src/main/java/jakarta/data/page/PageRequest.java
index 7a1e4cf7..0244b47a 100644
--- a/api/src/main/java/jakarta/data/page/PageRequest.java
+++ b/api/src/main/java/jakarta/data/page/PageRequest.java
@@ -131,60 +131,63 @@ public interface PageRequest<T> {
     }
 
     /**
-     * <p>Requests {@link CursoredPage keyset pagination} in the forward direction,
-     * starting after the specified keyset values.</p>
+     * <p>Requests {@link CursoredPage cursor-based pagination} in the forward direction,
+     * starting after the specified key.</p>
      *
-     * @param keyset keyset values, the order and number of which must match the
+     * @param key values that combined together form the key,
+     *            the order and number of which must match the
      *        {@link OrderBy} annotations, {@link Sort} parameters, or
      *        {@code OrderBy} name pattern of the repository method to which
      *        this pagination will be supplied.
-     * @return a new instance of {@code PageRequest} with forward keyset pagination.
+     * @return a new instance of {@code PageRequest} with forward cursor-based pagination.
      *         This method never returns {@code null}.
-     * @throws IllegalArgumentException if no keyset values are provided.
+     * @throws IllegalArgumentException if no values are provided for the key.
      */
-    PageRequest<T> afterKeyset(Object... keyset);
+    PageRequest<T> afterKey(Object... key);
 
     /**
-     * <p>Requests {@link CursoredPage keyset pagination} in the reverse direction,
-     * starting after the specified keyset values.</p>
+     * <p>Requests {@link CursoredPage cursor-based pagination} in the previous page
+     * direction relative to the specified key.</p>
      *
-     * @param keyset keyset values, the order and number of which must match the
+     * @param key values that combined together form the key,
+     *            the order and number of which must match the
      *        {@link OrderBy} annotations, {@link Sort} parameters, or
      *        {@code OrderBy} name pattern of the repository method to which
      *        this pagination will be supplied.
-     * @return a new instance of {@code PageRequest} with reverse keyset pagination.
+     * @return a new instance of {@code PageRequest} with cursor-based pagination
+     *         in the previous page direction.
      *         This method never returns {@code null}.
-     * @throws IllegalArgumentException if no keyset values are provided.
+     * @throws IllegalArgumentException if no values are provided for the key.
      */
-    PageRequest<T> beforeKeyset(Object... keyset);
+    PageRequest<T> beforeKey(Object... key);
 
     /**
-     * <p>Requests {@link CursoredPage keyset pagination} in the forward direction,
-     * starting after the specified keyset values.</p>
+     * <p>Requests {@link CursoredPage cursor-based pagination} in the forward direction,
+     * starting after the specified key.</p>
      *
-     * @param keysetCursor cursor with keyset values, the order and number of which must match the
+     * @param cursor cursor with key values, the order and number of which must match the
      *        {@link OrderBy} annotations, {@link Sort} parameters, or
      *        {@code OrderBy} name pattern of the repository method to which
      *        this pagination will be supplied.
-     * @return a new instance of {@code PageRequest} with forward keyset pagination.
+     * @return a new instance of {@code PageRequest} with forward cursor-based pagination.
      *         This method never returns {@code null}.
-     * @throws IllegalArgumentException if no keyset values are provided.
+     * @throws IllegalArgumentException if no key values are provided.
      */
-    PageRequest<T> afterKeysetCursor(Cursor keysetCursor);
+    PageRequest<T> afterCursor(Cursor cursor);
 
     /**
-     * <p>Requests {@link CursoredPage keyset pagination} in the reverse direction,
-     * starting after the specified keyset values.</p>
+     * <p>Requests {@link CursoredPage cursor-based pagination} in the previous page
+     * direction relative to the specified key values.</p>
      *
-     * @param keysetCursor cursor with keyset values, the order and number of which must match the
+     * @param cursor cursor with key values, the order and number of which must match the
      *        {@link OrderBy} annotations, {@link Sort} parameters, or
      *        {@code OrderBy} name pattern of the repository method to which
      *        this pagination will be supplied.
-     * @return a new instance of {@code PageRequest} with reverse keyset pagination.
-     *         This method never returns {@code null}.
-     * @throws IllegalArgumentException if no keyset values are provided.
+     * @return a new instance of {@code PageRequest} with cursor-based pagination
+     *         in the previous page direction. This method never returns {@code null}.
+     * @throws IllegalArgumentException if no key values are provided.
      */
-    PageRequest<T> beforeKeysetCursor(Cursor keysetCursor);
+    PageRequest<T> beforeCursor(Cursor cursor);
 
     /**
      * <p>Creates a new page request with the same pagination information,
@@ -251,10 +254,10 @@ public interface PageRequest<T> {
     boolean equals(Object o);
 
     /**
-     * Returns the keyset values which are the starting point for
-     * keyset pagination.
+     * Returns the key values which are the starting point for
+     * cursor-based pagination.
      *
-     * @return the keyset values; {@link Optional#empty()} if using offset pagination.
+     * @return the cursor; {@link Optional#empty()} if using offset pagination.
      */
     Optional<Cursor> cursor();
 
@@ -302,10 +305,10 @@ public interface PageRequest<T> {
      * <p>Returns the {@code PageRequest} requesting the next page if
      * using offset pagination.</p>
      *
-     * <p>If using keyset pagination, traversal of pages must only be done
+     * <p>If using cursor-based pagination, traversal of pages must only be done
      * via the {@link CursoredPage#nextPageRequest()},
      * {@link CursoredPage#previousPageRequest()}, or
-     * {@linkplain CursoredPage#getKeysetCursor(int) keyset cursor},
+     * {@linkplain CursoredPage#getCursor(int) cursor},
      * not with this method.</p>
      *
      * @return The next PageRequest.
@@ -319,10 +322,10 @@ public interface PageRequest<T> {
      * if using offset pagination, or null if this is the first page, that
      * is, when {@link #page()} returns {@code 1}.</p>
      *
-     * <p>If using keyset pagination, traversal of pages must only be done
+     * <p>If using cursor-based pagination, traversal of pages must only be done
      * via the {@link CursoredPage#nextPageRequest()},
      * {@link CursoredPage#previousPageRequest()}, or
-     * {@linkplain CursoredPage#getKeysetCursor(int) keyset cursor},
+     * {@linkplain CursoredPage#getCursor(int) cursor},
      * not with this method.</p>
      *
      * @return The previous PageRequest, or null if this is the first page.
@@ -477,21 +480,21 @@ public interface PageRequest<T> {
     PageRequest<T> withTotal();
 
     /**
-     * The type of pagination: offset-based or keyset cursor-based,
-     * which includes a direction.
+     * The type of pagination: offset-based or cursor-based, which includes
+     * a direction.
      */
     enum Mode {
         /**
-         * Indicates forward keyset pagination, which follows the
+         * Indicates forward cursor-based pagination, which follows the
          * direction of the sort criteria, using a cursor that is
-         * formed from the keyset of the last entity on the current page.
+         * formed from the key of the last entity on the current page.
          */
         CURSOR_NEXT,
 
         /**
-         * Indicates a request for a page with keyset pagination
-         * in the reverse direction of the sort criteria, using a cursor
-         * that is formed from the keyset of first entity on the current page.
+         * Indicates a request for a page with cursor-based pagination
+         * in the previous page direction to the sort criteria, using a cursor
+         * that is formed from the key of first entity on the current page.
          * The order of results on each page follows the sort criteria
          * and is not reversed.
          */
@@ -507,49 +510,49 @@ public interface PageRequest<T> {
     }
 
     /**
-     * A cursor that is formed from a keyset, relative to which a next
+     * A cursor that is formed from a key, relative to which a next
      * or previous page can be requested.
      */
     interface Cursor {
         /**
-         * Returns whether or not the keyset values of this cursor
+         * Returns whether or not the values that make up the key of this cursor
          * are equal to those of the supplied cursor.
          * Both instances must also have the same cursor implementation class
          * in order to be considered equal.
          *
-         * @param cursor a keyset cursor against which to compare.
+         * @param cursor a cursor against which to compare.
          * @return true or false.
          */
         @Override
         boolean equals(Object cursor);
 
         /**
-         * Returns the keyset value at the specified position.
+         * Returns the key value at the specified position.
          *
-         * @param  index position (0 is first) of the keyset value to obtain.
-         * @return the keyset value at the specified position.
+         * @param  index position (0 is first) of the key value to obtain.
+         * @return the key value at the specified position.
          * @throws IndexOutOfBoundsException if the index is negative
          *         or greater than or equal to the {@link #size}.
          */
-        Object getKeysetElement(int index);
+        Object get(int index);
 
         /**
-         * Returns a hash code based on the keyset values.
+         * Returns a hash code based on the key values.
          *
-         * @return a hash code based on the keyset values.
+         * @return a hash code based on the key values.
          */
         @Override
         int hashCode();
 
         /**
-         * Returns the number of values in the keyset.
+         * Returns the number of values in the key.
          *
-         * @return the number of values in the keyset.
+         * @return the number of values in the key.
          */
         int size();
 
         /**
-         * An unmodifiable list of values in the keyset.
+         * An unmodifiable list of values in the key.
          *
          * @return an unmodifiable list containing the
          *         ordered values
@@ -557,21 +560,21 @@ public interface PageRequest<T> {
         List<?> elements();
 
         /**
-         * String representation of the keyset cursor, including the number of
+         * String representation of the cursor, including the number of
          * key values in the cursor but not the values themselves.
          *
-         * @return String representation of the keyset cursor.
+         * @return String representation of the cursor.
          */
         @Override
         String toString();
 
         /**
-         * Obtain an instance of {@code Cursor} for the given keyset.
-         * @param keyset the keyset
+         * Obtain an instance of {@code Cursor} for the given key.
+         * @param key the key
          * @return a new instance of {@code Cursor}
          */
-        static Cursor forKeyset(Object... keyset) {
-            return new PageRequestCursor(keyset);
+        static Cursor forKey(Object... key) {
+            return new PageRequestCursor(key);
         }
     }
 }
\ No newline at end of file
diff --git a/api/src/main/java/jakarta/data/page/PageRequestCursor.java b/api/src/main/java/jakarta/data/page/PageRequestCursor.java
index db476170..0b835d27 100644
--- a/api/src/main/java/jakarta/data/page/PageRequestCursor.java
+++ b/api/src/main/java/jakarta/data/page/PageRequestCursor.java
@@ -21,55 +21,55 @@ import java.util.Arrays;
 import java.util.List;
 
 /**
- * Built-in implementation of Cursor for keyset pagination.
+ * Built-in implementation of Cursor for cursor-based pagination.
  */
 class PageRequestCursor implements PageRequest.Cursor {
     /**
-     * Keyset values.
+     * Key values.
      */
-    private final Object[] keyset;
+    private final Object[] key;
 
     /**
-     * Constructs a keyset cursor with the specified values.
+     * Constructs a cursor with the specified key values.
      *
-     * @param keyset keyset values.
-     * @throws IllegalArgumentException if no keyset values are provided.
+     * @param key key values.
+     * @throws IllegalArgumentException if no key values are provided.
      */
-    PageRequestCursor(Object... keyset) {
-        this.keyset = keyset;
-        if (keyset == null || keyset.length == 0)
-            throw new IllegalArgumentException("No keyset values were provided.");
+    PageRequestCursor(Object... key) {
+        this.key = key;
+        if (key == null || key.length == 0)
+            throw new IllegalArgumentException("No key values were provided.");
     }
 
     @Override
     public boolean equals(Object o) {
         return this == o || o != null
                 && o.getClass() == getClass()
-                && Arrays.equals(keyset, ((PageRequestCursor) o).keyset);
+                && Arrays.equals(key, ((PageRequestCursor) o).key);
     }
 
-    public Object getKeysetElement(int index) {
-        return keyset[index];
+    public Object get(int index) {
+        return key[index];
     }
 
     @Override
     public int hashCode() {
-        return Arrays.hashCode(keyset);
+        return Arrays.hashCode(key);
     }
 
     public int size() {
-        return keyset.length;
+        return key.length;
     }
 
     @Override
     public List<?> elements() {
-        return List.of(keyset);
+        return List.of(key);
     }
 
     @Override
     public String toString() {
         return new StringBuilder(27).append("Cursor@").append(Integer.toHexString(hashCode()))
-                        .append(" with ").append(keyset.length).append(" keys")
+                        .append(" with ").append(key.length).append(" keys")
                         .toString();
     }
 }
diff --git a/api/src/main/java/jakarta/data/page/Pagination.java b/api/src/main/java/jakarta/data/page/Pagination.java
index ddad618a..26d78b28 100644
--- a/api/src/main/java/jakarta/data/page/Pagination.java
+++ b/api/src/main/java/jakarta/data/page/Pagination.java
@@ -39,7 +39,7 @@ record Pagination<T>(long page, int size, List<Sort<? super T>> sorts, Mode mode
         }
 
         if (mode != Mode.OFFSET && (type == null || type.size() == 0)) {
-            throw new IllegalArgumentException("No keyset values were provided.");
+            throw new IllegalArgumentException("No key values were provided.");
         }
     }
 
@@ -54,23 +54,23 @@ record Pagination<T>(long page, int size, List<Sort<? super T>> sorts, Mode mode
     }
 
     @Override
-    public PageRequest<T> afterKeyset(Object... keyset) {
-        return new Pagination<T>(page, size, sorts, Mode.CURSOR_NEXT, new PageRequestCursor(keyset), requestTotal);
+    public PageRequest<T> afterKey(Object... key) {
+        return new Pagination<T>(page, size, sorts, Mode.CURSOR_NEXT, new PageRequestCursor(key), requestTotal);
     }
 
     @Override
-    public PageRequest<T> beforeKeyset(Object... keyset) {
-        return new Pagination<T>(page, size, sorts, Mode.CURSOR_PREVIOUS, new PageRequestCursor(keyset), requestTotal);
+    public PageRequest<T> beforeKey(Object... key) {
+        return new Pagination<T>(page, size, sorts, Mode.CURSOR_PREVIOUS, new PageRequestCursor(key), requestTotal);
     }
 
     @Override
-    public PageRequest<T> afterKeysetCursor(Cursor keysetCursor) {
-        return new Pagination<T>(page, size, sorts, Mode.CURSOR_NEXT, keysetCursor, requestTotal);
+    public PageRequest<T> afterCursor(Cursor cursor) {
+        return new Pagination<T>(page, size, sorts, Mode.CURSOR_NEXT, cursor, requestTotal);
     }
 
     @Override
-    public PageRequest<T> beforeKeysetCursor(Cursor keysetCursor) {
-        return new Pagination<T>(page, size, sorts, Mode.CURSOR_PREVIOUS, keysetCursor, requestTotal);
+    public PageRequest<T> beforeCursor(Cursor cursor) {
+        return new Pagination<T>(page, size, sorts, Mode.CURSOR_PREVIOUS, cursor, requestTotal);
     }
 
     @Override
@@ -116,8 +116,8 @@ record Pagination<T>(long page, int size, List<Sort<? super T>> sorts, Mode mode
         if (mode == Mode.OFFSET) {
             return new Pagination<T>(page + 1, this.size, this.sorts, Mode.OFFSET, null, requestTotal);
         } else {
-            throw new UnsupportedOperationException("Not supported for keyset pagination. Instead use afterKeyset or afterKeysetCursor " +
-                    "to provide the next keyset values or obtain the nextPageRequest from a CursoredPage.");
+            throw new UnsupportedOperationException("Not supported for cursor-based pagination. Instead use afterKey or afterCursor " +
+                    "to provide a cursor or obtain the nextPageRequest from a CursoredPage.");
         }
     }
 
@@ -126,8 +126,8 @@ record Pagination<T>(long page, int size, List<Sort<? super T>> sorts, Mode mode
         if (mode == Mode.OFFSET) {
             return page()<=1 ? null : new Pagination<T>(page - 1, this.size, this.sorts, Mode.OFFSET, null, requestTotal);
         } else {
-            throw new UnsupportedOperationException("Not supported for keyset pagination. Instead use beforeKeyset or beforeKeysetCursor " +
-                    "to provide the previous keyset values or obtain the previousPageRequest from a CursoredPage.");
+            throw new UnsupportedOperationException("Not supported for cursor-based pagination. Instead use beforeKey or beforeCursor " +
+                    "to provide a cursor or obtain the previousPageRequest from a CursoredPage.");
         }
     }
 
diff --git a/api/src/main/java/jakarta/data/page/impl/CursoredPageRecord.java b/api/src/main/java/jakarta/data/page/impl/CursoredPageRecord.java
index 7bd5ccfc..55cdbdf1 100644
--- a/api/src/main/java/jakarta/data/page/impl/CursoredPageRecord.java
+++ b/api/src/main/java/jakarta/data/page/impl/CursoredPageRecord.java
@@ -104,7 +104,7 @@ public record CursoredPageRecord<T>
     }
 
     @Override
-    public PageRequest.Cursor getKeysetCursor(int index) {
+    public PageRequest.Cursor getCursor(int index) {
         return cursors.get(index);
     }
 
diff --git a/spec/src/main/asciidoc/repository.asciidoc b/spec/src/main/asciidoc/repository.asciidoc
index 1b0d61c3..e76daab5 100644
--- a/spec/src/main/asciidoc/repository.asciidoc
+++ b/spec/src/main/asciidoc/repository.asciidoc
@@ -1201,17 +1201,17 @@ CursoredPage<User> olderThan(int age, PageRequest<User> pagination);
 
 Dividing up large sets of data into pages is a beneficial strategy for data access and retrieval in many applications, including those developed in Java. Pagination helps improve the efficiency of handling large datasets in a way that is also user-friendly. In Jakarta Data, APIs are provided to help Java developers efficiently manage and navigate through data.
 
-Jakarta Data supports two types of pagination: Offset and Keyset. These approaches differ in how they manage and retrieve paginated data:
+Jakarta Data supports two types of pagination: offset-based and cursor-based. These approaches differ in how they manage and retrieve paginated data:
 
 Offset pagination is the more traditional form based on position relative to the first record in the dataset. It is typically used with a fixed page size, where a specified number of records is retrieved starting from a given offset position.
 
-Keyset pagination, also known as seek method or cursor-based pagination, uses a unique key or unique combination of values (referred to as the keyset) to navigate the dataset relative to the first or last record of the current page. Keyset pagination is typically used with fixed page sizes but can accommodate varying the page size if desired. It is more robust when dealing with datasets where the underlying data might change and offers the the potential for improved performance by avoiding the need to scan records prior to the cursor.
+Cursor-based pagination, also known as seek method or keyset pagination, uses a unique key or unique combination of values (referred to as the key) to navigate the dataset relative to the first or last record of the current page. Cursor-based pagination is typically used with fixed page sizes but can accommodate varying the page size if desired. It is more robust when dealing with datasets where the underlying data might change and offers the the potential for improved performance by avoiding the need to scan records prior to the cursor.
 
 
-The critical differences between Offset and Keyset pagination lie in their retrieval methods:
+The critical differences between offset-based and cursor-based pagination lie in their retrieval methods:
 
-- Offset pagination uses a fixed page size and retrieves data based on page number and size.
-- Keyset pagination relies on a unique key or unique combination of values (the keyset) for an entity relative to which it determines the next page or previous page.
+- Offset-based pagination uses a fixed page size and retrieves data based on page number and size.
+- Cursor-based pagination relies on a unique key or unique combination of values (the key) for an entity relative to which it determines the next page or previous page.
 
 ==== Offset Pagination in Jakarta Data
 
@@ -1331,9 +1331,9 @@ In this scenario, each page represents a subset of the dataset, and developers c
 
 Offset pagination is a valuable tool for Java developers when dealing with large datasets, providing control, efficiency, and a seamless user experience.
 
-==== Keyset Pagination
+==== Cursor-based Pagination
 
-Keyset pagination aims to reduce missed and duplicate results across pages by querying relative to the observed values of entity properties that constitute the sorting criteria. Keyset pagination can also offer an improvement in performance because it avoids fetching and ordering results from prior pages by causing those results to be non-matching. A Jakarta Data provider appends additional conditions to the query and tracks keyset values automatically when `CursoredPage` is used as the repository method return type. The application invokes `nextPageRequest` or `previousPageRequest` on the keyset aware slice or page to obtain a `PageRequest` which keeps track of the keyset values.
+Cursor-based pagination aims to reduce missed and duplicate results across pages by querying relative to the observed values of entity properties that constitute the sorting criteria. Cursor-based pagination can also offer an improvement in performance because it avoids fetching and ordering results from prior pages by causing those results to be non-matching. A Jakarta Data provider appends additional conditions to the query and tracks cursor-based values automatically when `CursoredPage` is used as the repository method return type. The application invokes `nextPageRequest` or `previousPageRequest` on the cursor-based aware slice or page to obtain a `PageRequest` which keeps track of the cursor-based values.
 
 For example,
 
@@ -1365,20 +1365,20 @@ Or you can obtain the next (or previous) page relative to a known entity,
 [source,java]
 ----
 Customer c = ...
-PageRequest<Customer> p = PageRequest.of(Customer.class).size(50).afterKeyset(c.lastName, c.firstName, c.id);
+PageRequest<Customer> p = PageRequest.of(Customer.class).size(50).afterKey(c.lastName, c.firstName, c.id);
 page = customers.findByZipcodeOrderByLastNameAscFirstNameAscIdAsc(55902, p);
 ----
 
-The sort criteria for a repository method that performs keyset pagination must uniquely identify each entity and must be provided by:
+The sort criteria for a repository method that performs cursor-based pagination must uniquely identify each entity and must be provided by:
 
 * `OrderBy` name pattern of the repository method (as in the examples above) or `@OrderBy` annotation(s) on the repository method.
 * `Sort` parameters of the `PageRequest` that is supplied to the repository method.
 
-The values of the entity attributes of the combined sort criteria define the cursor for keyset cursor based pagination. Within the cursor, each entity attribute has the same sorting and order of precedence that it has within the combined sort criteria.
+The values of the entity attributes of the combined sort criteria define the cursor for cursor-based cursor based pagination. Within the cursor, each entity attribute has the same sorting and order of precedence that it has within the combined sort criteria.
 
-===== Example of Appending to Queries for Keyset Pagination
+===== Example of Appending to Queries for Cursor-based Pagination
 
-Without keyset pagination, a Jakarta Data provider that is based on Jakarta Persistence might compose the following JPQL for the `findByZipcodeOrderByLastNameAscFirstNameAscIdAsc` repository method from the prior example:
+Without cursor-based pagination, a Jakarta Data provider that is based on Jakarta Persistence might compose the following JPQL for the `findByZipcodeOrderByLastNameAscFirstNameAscIdAsc` repository method from the prior example:
 
 [source,jpql]
 ----
@@ -1386,7 +1386,7 @@ SELECT o FROM Customer o WHERE (o.zipCode = ?1)
                          ORDER BY o.lastName ASC, o.firstName ASC, o.id ASC
 ----
 
-When keyset pagination is used, the keyset values from the `Cursor` of the `PageRequest` are available as query parameters, allowing the Jakarta Data provider to append additional query conditions. For example,
+When cursor-based pagination is used, the keys values from the `Cursor` of the `PageRequest` are available as query parameters, allowing the Jakarta Data provider to append additional query conditions. For example,
 
 [source,jpql]
 ----
@@ -1400,23 +1400,23 @@ SELECT o FROM Customer o WHERE (o.zipCode = ?1)
 
 ===== Avoiding Missed and Duplicate Results
 
-Because searching for the next page of results is relative to a last known position, it is possible with keyset pagination to allow some types of updates to data while pages are being traversed without causing missed results or duplicates to appear. If you add entities to a prior position in the traversal of pages, the shift forward of numerical position of existing entities will not cause duplicates entities to appear in your continued traversal of subsequent pages because keyset pagination does not query based on a numerical position. If you remove entities from a prior position in the traversal of pages, the shift backward of numerical position of existing entities will not cause missed entities in your continued traversal of subsequent pages because keyset pagination does not query based on a numerical position.
+Because searching for the next page of results is relative to a last known position, it is possible with cursor-based pagination to allow some types of updates to data while pages are being traversed without causing missed results or duplicates to appear. If you add entities to a prior position in the traversal of pages, the shift forward of numerical position of existing entities will not cause duplicates entities to appear in your continued traversal of subsequent pages because cursor-based pagination does not query based on a numerical position. If you remove entities from a prior position in the traversal of pages, the shift backward of numerical position of existing entities will not cause missed entities in your continued traversal of subsequent pages because keyset pagination does not query based on a numerical position.
 
-Other types of updates to data, however, will cause duplicate or missed results. If you modify entity properties which are used as the sort criteria, keyset pagination cannot prevent the same entity from appearing again or never appearing due to the altered values. If you add an entity that you previously removed, whether with different values or the same values, keyset pagination cannot prevent the entity from being missed or possibly appearing a second time due to its changed values.
+Other types of updates to data, however, will cause duplicate or missed results. If you modify entity properties which are used as the sort criteria, cursor-based pagination cannot prevent the same entity from appearing again or never appearing due to the altered values. If you add an entity that you previously removed, whether with different values or the same values, cursor-based pagination cannot prevent the entity from being missed or possibly appearing a second time due to its changed values.
 
-===== Restrictions on use of Keyset Pagination
+===== Restrictions on use of Cursor-based Pagination
 
-* The repository method signature must return `CursoredPage`. A repository method with return type of `CursoredPage` must raise `UnsupportedOperationException` if the database is incapable of keyset pagination.
+* The repository method signature must return `CursoredPage`. A repository method with return type of `CursoredPage` must raise `UnsupportedOperationException` if the database is incapable of cursor-based pagination.
 * The repository method signature must accept a `PageRequest` parameter.
 * Sort criteria must be provided and should be minimal.
 * The combination of provided sort criteria must uniquely identify each entity such that the sort criteria defines a deterministic ordering of entities.
 * The entities within each page must be ordered according to the provided sort criteria.
-* Page numbers for keyset pagination are estimated relative to prior page requests or the observed absence of further results and are not accurate. Page numbers must not be relied upon when using keyset pagination.
-* Page totals and result totals are not accurate for keyset pagination and must not be relied upon.
-* A next or previous page can end up being empty. You cannot obtain a next or previous `PageRequest` from an empty page because there are no keyset values relative to which to query.
-* A repository method that is annotated with `@Query` and performs keyset pagination must omit the `ORDER BY` clause from the provided query and instead must supply the sort criteria via `@OrderBy` annotations or `Sort` criteria of `PageRequest`. The provided query must end with a `WHERE` clause to which additional conditions can be appended by the Jakarta Data provider. The Jakarta Data provider is not expected to parse query text that is provided by the application.
+* Page numbers for cursor-based pagination are estimated relative to prior page requests or the observed absence of further results and are not accurate. Page numbers must not be relied upon when using cursor-based pagination.
+* Page totals and result totals are not accurate for cursor-based pagination and must not be relied upon.
+* A next or previous page can end up being empty. You cannot obtain a next or previous `PageRequest` from an empty page because there are no key values relative to which to query.
+* A repository method that is annotated with `@Query` and performs cursor-based pagination must omit the `ORDER BY` clause from the provided query and instead must supply the sort criteria via `@OrderBy` annotations or `Sort` criteria of `PageRequest`. The provided query must end with a `WHERE` clause to which additional conditions can be appended by the Jakarta Data provider. The Jakarta Data provider is not expected to parse query text that is provided by the application.
 
-===== Keyset Pagination Example with Sorts
+===== Cursor-based Pagination Example with Sorts
 
 Here is an example where an application uses `@Query` to provide a partial query to which the Jakarta Data provider can generate and append additional query conditions and an `ORDER BY` clause.
 
@@ -1464,7 +1464,7 @@ Obtaining the next 10 products that cost $50.00 or more:
 float priceMidpoint = 50.0f;
 PageRequest<Product> pageRequest = Order.by(_Product.price.asc(), _Product.id.asc())
                                         .pageSize(10)
-                                        .afterKeyset(priceMidpoint, 0L);
+                                        .afterKey(priceMidpoint, 0L);
 CursoredPage<Product> moreExpensive = products.findByNameContains(pattern, pageRequest);
 ----
 
@@ -1473,9 +1473,9 @@ Obtaining the previous 10 products:
 [source,java]
 ----
 if (moreExpensive.hasContent())
-    pageRequest = pageRequest.beforeKeysetCursor(moreExpensive.getKeysetCursor(0));
+    pageRequest = pageRequest.beforeCursor(moreExpensive.getCursor(0));
 else
-    pageRequest = pageRequest.beforeKeyset(priceMidpoint, 1L);
+    pageRequest = pageRequest.beforeKey(priceMidpoint, 1L);
 CursoredPage<Product> lessExpensive = products.findByNameContains(pattern, pageRequest);
 ----
 
@@ -1510,15 +1510,15 @@ The query results are ordered first by vehicle condition. All resulting entities
 PageRequest<Car> page2Request = Order.by(_Car.price.desc(), _Car.vin.asc())
                                      .page(2) // cosmetic when using a cursor
                                      .size(25)
-                                     .afterKeyset(lastCar.vehicleCondition,
-                                                  lastCar.price,
-                                                  lastCar.vin);
+                                     .afterKey(lastCar.vehicleCondition,
+                                               lastCar.price,
+                                               lastCar.vin);
 CursoredPage<Car> page2 = cars.find(make, model, page2Request);
 ----
 
 ===== Scenario: Person Entity and People Repository
 
-This keyset cursor-based pagination scenario uses the same `Person` entity and example dataset from the offset-based pagination scenario, but orders it by `name` and then by `id`,
+This cursor-based pagination scenario uses the same `Person` entity and example dataset from the offset-based pagination scenario, but orders it by `name` and then by `id`,
 
 [source,json]
 ----
@@ -1603,7 +1603,7 @@ Resulting Page Content:
 ]
 ----
 
-It should be noted, the above result is different than what would be retrieved with offset-based pagination, where the removal of an entity from the first page shifts the offset for entries 5 through 8 to start from `{"id":9, "name":"Harlene Branigan"}`, skipping over `{"id":8, "name":"Danita Pilipyak"}` that becomes offset position 4 after the removal. Keyset cursor-based pagination does not skip the entity because it queries relative to a cursor position, starting from the next entity after `{"id":2, "name":"Corri Davidou"}`.
+It should be noted, the above result is different than what would be retrieved with offset-based pagination, where the removal of an entity from the first page shifts the offset for entries 5 through 8 to start from `{"id":9, "name":"Harlene Branigan"}`, skipping over `{"id":8, "name":"Danita Pilipyak"}` that becomes offset position 4 after the removal. Cursor-based pagination does not skip the entity because it queries relative to a cursor position, starting from the next entity after `{"id":2, "name":"Corri Davidou"}`.
 
 === Precedence of Repository Methods
 


Test patch:
diff --git a/api/src/test/java/jakarta/data/page/KeysetPageRequestTest.java b/api/src/test/java/jakarta/data/page/KeysetPageRequestTest.java
index 6fca46ed..7daa27d7 100644
--- a/api/src/test/java/jakarta/data/page/KeysetPageRequestTest.java
+++ b/api/src/test/java/jakarta/data/page/KeysetPageRequestTest.java
@@ -32,26 +32,26 @@ import static org.assertj.core.api.SoftAssertions.assertSoftly;
 class KeysetPageRequestTest {
 
     @Test
-    @DisplayName("Should include keyset values in next PageRequest")
+    @DisplayName("Should include key values in next PageRequest")
     void shouldCreatePageRequestAfterKeyset() {
-        PageRequest<?> pageRequest = PageRequest.ofSize(20).afterKeyset("First", 2L, 3);
+        PageRequest<?> pageRequest = PageRequest.ofSize(20).afterKey("First", 2L, 3);
 
         assertSoftly(softly -> {
             softly.assertThat(pageRequest.size()).isEqualTo(20);
             softly.assertThat(pageRequest.page()).isEqualTo(1L);
             softly.assertThat(pageRequest.mode()).isEqualTo(PageRequest.Mode.CURSOR_NEXT);
             softly.assertThat(pageRequest.cursor()).get().extracting(PageRequest.Cursor::size).isEqualTo(3);
-            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.getKeysetElement(0)).isEqualTo("First");
-            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.getKeysetElement(1)).isEqualTo(2L);
-            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.getKeysetElement(2)).isEqualTo(3);
+            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.get(0)).isEqualTo("First");
+            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.get(1)).isEqualTo(2L);
+            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.get(2)).isEqualTo(3);
         });
     }
 
     @Test
-    @DisplayName("Should include keyset values in next PageRequest from Cursor")
+    @DisplayName("Should include key values in next PageRequest from Cursor")
     void shouldCreatePageRequestAfterKeysetCursor() {
         PageRequest.Cursor cursor = new PageRequestCursor("me", 200);
-        PageRequest<?> pageRequest = PageRequest.ofSize(35).sortBy(Sort.asc("name"), Sort.asc("id")).afterKeysetCursor(cursor);
+        PageRequest<?> pageRequest = PageRequest.ofSize(35).sortBy(Sort.asc("name"), Sort.asc("id")).afterCursor(cursor);
 
         assertSoftly(softly -> {
             softly.assertThat(pageRequest.size()).isEqualTo(35);
@@ -59,15 +59,15 @@ class KeysetPageRequestTest {
             softly.assertThat(pageRequest.sorts()).isEqualTo(List.of(Sort.asc("name"), Sort.asc("id")));
             softly.assertThat(pageRequest.mode()).isEqualTo(PageRequest.Mode.CURSOR_NEXT);
             softly.assertThat(pageRequest.cursor()).get().extracting(PageRequest.Cursor::size).isEqualTo(2);
-            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.getKeysetElement(0)).isEqualTo("me");
-            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.getKeysetElement(1)).isEqualTo(200);
+            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.get(0)).isEqualTo("me");
+            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.get(1)).isEqualTo(200);
         });
     }
 
     @Test
-    @DisplayName("Should include keyset values in previous PageRequest")
+    @DisplayName("Should include key values in previous PageRequest")
     void shouldCreatePageRequestBeforeKeyset() {
-        PageRequest<?> pageRequest = PageRequest.ofSize(30).sortBy(Sort.desc("yearBorn"), Sort.asc("ssn")).beforeKeyset(1991, "123-45-6789").page(10);
+        PageRequest<?> pageRequest = PageRequest.ofSize(30).sortBy(Sort.desc("yearBorn"), Sort.asc("ssn")).beforeKey(1991, "123-45-6789").page(10);
 
         assertSoftly(softly -> {
             softly.assertThat(pageRequest.size()).isEqualTo(30);
@@ -75,16 +75,16 @@ class KeysetPageRequestTest {
             softly.assertThat(pageRequest.sorts()).isEqualTo(List.of(Sort.desc("yearBorn"), Sort.asc("ssn")));
             softly.assertThat(pageRequest.mode()).isEqualTo(PageRequest.Mode.CURSOR_PREVIOUS);
             softly.assertThat(pageRequest.cursor()).get().extracting(PageRequest.Cursor::size).isEqualTo(2);
-            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.getKeysetElement(0)).isEqualTo(1991);
-            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.getKeysetElement(1)).isEqualTo("123-45-6789");
+            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.get(0)).isEqualTo(1991);
+            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.get(1)).isEqualTo("123-45-6789");
         });
     }
 
     @Test
-    @DisplayName("Should include keyset values in previous PageRequest from Cursor")
+    @DisplayName("Should include key values in previous PageRequest from Cursor")
     void shouldCreatePageRequestBeforeKeysetCursor() {
         PageRequest.Cursor cursor = new PageRequestCursor(900L, 300, "testing", 120, 'T');
-        PageRequest<?> pageRequest = PageRequest.ofPage(8).beforeKeysetCursor(cursor);
+        PageRequest<?> pageRequest = PageRequest.ofPage(8).beforeCursor(cursor);
 
         assertSoftly(softly -> {
             softly.assertThat(pageRequest.size()).isEqualTo(10);
@@ -92,22 +92,22 @@ class KeysetPageRequestTest {
             softly.assertThat(pageRequest.sorts()).isEqualTo(Collections.EMPTY_LIST);
             softly.assertThat(pageRequest.mode()).isEqualTo(PageRequest.Mode.CURSOR_PREVIOUS);
             softly.assertThat(pageRequest.cursor()).get().extracting(PageRequest.Cursor::size).isEqualTo(5);
-            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.getKeysetElement(0)).isEqualTo(900L);
-            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.getKeysetElement(1)).isEqualTo(300);
-            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.getKeysetElement(2)).isEqualTo("testing");
-            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.getKeysetElement(3)).isEqualTo(120);
-            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.getKeysetElement(4)).isEqualTo('T');
+            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.get(0)).isEqualTo(900L);
+            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.get(1)).isEqualTo(300);
+            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.get(2)).isEqualTo("testing");
+            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.get(3)).isEqualTo(120);
+            softly.assertThat(pageRequest.cursor()).get().extracting(c -> c.get(4)).isEqualTo('T');
         });
     }
 
     @Test
     @DisplayName("Should be usable in a hashing structure")
     void shouldHash() {
-        PageRequest<?> pageRequest1 = PageRequest.ofSize(15).afterKeyset(1, '1', "1")
+        PageRequest<?> pageRequest1 = PageRequest.ofSize(15).afterKey(1, '1', "1")
                                               .sortBy(Sort.desc("yearHired"), Sort.asc("lastName"), Sort.asc("id"));
-        PageRequest<?> pageRequest2A = PageRequest.ofSize(15).afterKeyset(2, '2', "2");
-        PageRequest<?> pageRequest2B = PageRequest.ofSize(15).beforeKeyset(2, '2', "2");
-        PageRequest<?> pageRequest2C = PageRequest.ofSize(15).beforeKeyset(2, '2', "2");
+        PageRequest<?> pageRequest2A = PageRequest.ofSize(15).afterKey(2, '2', "2");
+        PageRequest<?> pageRequest2B = PageRequest.ofSize(15).beforeKey(2, '2', "2");
+        PageRequest<?> pageRequest2C = PageRequest.ofSize(15).beforeKey(2, '2', "2");
         Map<PageRequest<?>, Integer> map = new HashMap<>();
 
         assertSoftly(softly -> {
@@ -127,28 +127,28 @@ class KeysetPageRequestTest {
     @Test
     @DisplayName("Should be displayable as String with toString")
     void shouldPageRequestDisplayAsString() {
-        PageRequest<?> pageRequest = PageRequest.ofSize(200).afterKeyset("value1", 1);
+        PageRequest<?> pageRequest = PageRequest.ofSize(200).afterKey("value1", 1);
 
         assertSoftly(softly -> softly.assertThat(pageRequest.toString())
               .isEqualTo("PageRequest{page=1, size=200, mode=CURSOR_NEXT, 2 keys}"));
 
         PageRequest<?> pageRequestWithSorts = PageRequest.ofSize(100).sortBy(Sort.desc("name"), Sort.asc("id"))
-                                             .beforeKeyset("Item1", 3456);
+                                             .beforeKey("Item1", 3456);
 
         assertSoftly(softly -> softly.assertThat(pageRequestWithSorts.toString())
               .isEqualTo("PageRequest{page=1, size=100, mode=CURSOR_PREVIOUS, 2 keys, name DESC, id ASC}"));
     }
 
     @Test
-    @DisplayName("Should return true from equals if keyset values and other properties are equal")
+    @DisplayName("Should return true from equals if key values and other properties are equal")
     void shouldBeEqualWithSameKeysetValues() {
-        PageRequest<?> pageRequest25P1S0A1 = PageRequest.ofSize(25).afterKeyset("keyval1", '2', 3);
-        PageRequest<?> pageRequest25P1S0B1 = PageRequest.ofSize(25).beforeKeyset("keyval1", '2', 3);
-        PageRequest<?> pageRequest25P1S0A1Match = PageRequest.ofSize(25).afterKeysetCursor(new PageRequestCursor("keyval1", '2', 3));
-        PageRequest<?> pageRequest25P2S0A1 = PageRequest.ofPage(2).size(25).afterKeysetCursor(new PageRequestCursor("keyval1", '2', 3));
-        PageRequest<?> pageRequest25P1S1A1 = PageRequest.ofSize(25).sortBy(Sort.desc("d"), Sort.asc("a"), Sort.asc("id")).afterKeyset("keyval1", '2', 3);
-        PageRequest<?> pageRequest25P1S2A1 = PageRequest.ofSize(25).sortBy(Sort.desc("d"), Sort.asc("a"), Sort.desc("id")).afterKeyset("keyval1", '2', 3);
-        PageRequest<?> pageRequest25P1S0A2 = PageRequest.ofSize(25).afterKeyset("keyval2", '2', 3);
+        PageRequest<?> pageRequest25P1S0A1 = PageRequest.ofSize(25).afterKey("keyval1", '2', 3);
+        PageRequest<?> pageRequest25P1S0B1 = PageRequest.ofSize(25).beforeKey("keyval1", '2', 3);
+        PageRequest<?> pageRequest25P1S0A1Match = PageRequest.ofSize(25).afterCursor(new PageRequestCursor("keyval1", '2', 3));
+        PageRequest<?> pageRequest25P2S0A1 = PageRequest.ofPage(2).size(25).afterCursor(new PageRequestCursor("keyval1", '2', 3));
+        PageRequest<?> pageRequest25P1S1A1 = PageRequest.ofSize(25).sortBy(Sort.desc("d"), Sort.asc("a"), Sort.asc("id")).afterKey("keyval1", '2', 3);
+        PageRequest<?> pageRequest25P1S2A1 = PageRequest.ofSize(25).sortBy(Sort.desc("d"), Sort.asc("a"), Sort.desc("id")).afterKey("keyval1", '2', 3);
+        PageRequest<?> pageRequest25P1S0A2 = PageRequest.ofSize(25).afterKey("keyval2", '2', 3);
 
         PageRequest.Cursor cursor1 = new PageRequestCursor("keyval1", '2', 3);
         PageRequest.Cursor cursor2 = new PageRequestCursor("keyval2", '2', 3);
@@ -157,7 +157,7 @@ class KeysetPageRequestTest {
             private final Object[] keyset = new Object[] { "keyval1", '2', 3 };
 
             @Override
-            public Object getKeysetElement(int index) {
+            public Object get(int index) {
                 return keyset[index];
             }
 
@@ -197,31 +197,31 @@ class KeysetPageRequestTest {
     }
 
     @Test
-    @DisplayName("Should raise IllegalArgumentException when keyset values are absent")
+    @DisplayName("Should raise IllegalArgumentException when key values are absent")
     void shouldRaiseErrorForMissingKeysetValues() {
-        assertThatIllegalArgumentException().isThrownBy(() -> PageRequest.ofSize(60).afterKeyset((Object[]) null));
-        assertThatIllegalArgumentException().isThrownBy(() -> PageRequest.ofSize(70).afterKeyset(new Object[0]));
-        assertThatIllegalArgumentException().isThrownBy(() -> PageRequest.ofSize(80).beforeKeyset((Object[]) null));
-        assertThatIllegalArgumentException().isThrownBy(() -> PageRequest.ofSize(90).beforeKeyset(new Object[0]));
+        assertThatIllegalArgumentException().isThrownBy(() -> PageRequest.ofSize(60).afterKey((Object[]) null));
+        assertThatIllegalArgumentException().isThrownBy(() -> PageRequest.ofSize(70).afterKey(new Object[0]));
+        assertThatIllegalArgumentException().isThrownBy(() -> PageRequest.ofSize(80).beforeKey((Object[]) null));
+        assertThatIllegalArgumentException().isThrownBy(() -> PageRequest.ofSize(90).beforeKey(new Object[0]));
     }
 
     @Test
-    @DisplayName("Keyset should be replaced on new instance of PageRequest")
+    @DisplayName("Key should be replaced on new instance of PageRequest")
     void shouldReplaceKeyset() {
         PageRequest<?> p1 = PageRequest.ofSize(30).sortBy(Sort.asc("lastName"), Sort.asc("firstName"), Sort.asc("id"))
-                                         .afterKeyset("last1", "fname1", 100).page(12);
-        PageRequest<?> p2 = p1.beforeKeyset("lname2", "fname2", 200);
+                                         .afterKey("last1", "fname1", 100).page(12);
+        PageRequest<?> p2 = p1.beforeKey("lname2", "fname2", 200);
 
         assertSoftly(softly -> {
             softly.assertThat(p1.mode()).isEqualTo(PageRequest.Mode.CURSOR_NEXT);
-            softly.assertThat(p1.cursor()).get().extracting(c -> c.getKeysetElement(0)).isEqualTo("last1");
-            softly.assertThat(p1.cursor()).get().extracting(c -> c.getKeysetElement(1)).isEqualTo("fname1");
-            softly.assertThat(p1.cursor()).get().extracting(c -> c.getKeysetElement(2)).isEqualTo(100);
+            softly.assertThat(p1.cursor()).get().extracting(c -> c.get(0)).isEqualTo("last1");
+            softly.assertThat(p1.cursor()).get().extracting(c -> c.get(1)).isEqualTo("fname1");
+            softly.assertThat(p1.cursor()).get().extracting(c -> c.get(2)).isEqualTo(100);
 
             softly.assertThat(p2.mode()).isEqualTo(PageRequest.Mode.CURSOR_PREVIOUS);
-            softly.assertThat(p2.cursor()).get().extracting(c -> c.getKeysetElement(0)).isEqualTo("lname2");
-            softly.assertThat(p2.cursor()).get().extracting(c -> c.getKeysetElement(1)).isEqualTo("fname2");
-            softly.assertThat(p2.cursor()).get().extracting(c -> c.getKeysetElement(2)).isEqualTo(200);
+            softly.assertThat(p2.cursor()).get().extracting(c -> c.get(0)).isEqualTo("lname2");
+            softly.assertThat(p2.cursor()).get().extracting(c -> c.get(1)).isEqualTo("fname2");
+            softly.assertThat(p2.cursor()).get().extracting(c -> c.get(2)).isEqualTo(200);
 
             softly.assertThat(p1.sorts()).isEqualTo(List.of(Sort.asc("lastName"), Sort.asc("firstName"), Sort.asc("id")));
             softly.assertThat(p2.sorts()).isEqualTo(List.of(Sort.asc("lastName"), Sort.asc("firstName"), Sort.asc("id")));
diff --git a/api/src/test/java/jakarta/data/page/PaginationTest.java b/api/src/test/java/jakarta/data/page/PaginationTest.java
index 774769b0..9198ec5a 100644
--- a/api/src/test/java/jakarta/data/page/PaginationTest.java
+++ b/api/src/test/java/jakarta/data/page/PaginationTest.java
@@ -28,7 +28,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
 class PaginationTest {
 
     @Test
-    @DisplayName("Should throw IllegalArgumentException when no keyset values were provided")
+    @DisplayName("Should throw IllegalArgumentException when no key values were provided")
     void shouldThrowExceptionWhenNoKeysetValuesWereProvided() {
         assertThatIllegalArgumentException()
                 .as("Mode must be different from OFFSET when cursor is null")
@@ -36,7 +36,7 @@ class PaginationTest {
     }
 
     @Test
-    @DisplayName("Should throw UnsupportedOperationException when keyset is not supported")
+    @DisplayName("Should throw UnsupportedOperationException when key is not supported")
     void shouldThrowExceptionWhenKeysetIsNotSupported() {
         assertThatThrownBy(() -> {
             Pagination<?> pagination = new Pagination<>(1, 10, Collections.emptyList(),
@@ -45,6 +45,6 @@ class PaginationTest {
                                                         true);
             pagination.next();
         }).isInstanceOf(UnsupportedOperationException.class)
-                .hasMessage("Not supported for keyset pagination. Instead use afterKeyset or afterKeysetCursor to provide the next keyset values or obtain the nextPageRequest from a CursoredPage.");
+                .hasMessage("Not supported for cursor-based pagination. Instead use afterKey or afterCursor to provide a cursor or obtain the nextPageRequest from a CursoredPage.");
     }
 }
diff --git a/tck/src/main/java/ee/jakarta/tck/data/standalone/entity/EntityTests.java b/tck/src/main/java/ee/jakarta/tck/data/standalone/entity/EntityTests.java
index dca5f103..94ae3547 100644
--- a/tck/src/main/java/ee/jakarta/tck/data/standalone/entity/EntityTests.java
+++ b/tck/src/main/java/ee/jakarta/tck/data/standalone/entity/EntityTests.java
@@ -772,7 +772,7 @@ public class EntityTests {
             page = positives.findByFloorOfSquareRootNotAndIdLessThanOrderByBitsRequiredDesc(4L, 33L, first8);
         } catch (MappingException x) {
             // Test passes: Jakarta Data providers must raise MappingException when the database
-            // is not capable of keyset pagination.
+            // is not capable of cursor-based pagination.
             return;
         }
 
@@ -785,7 +785,7 @@ public class EntityTests {
             page = positives.findByFloorOfSquareRootNotAndIdLessThanOrderByBitsRequiredDesc(4L, 33L, page.nextPageRequest());
         } catch (MappingException x) {
             // Test passes: Jakarta Data providers must raise MappingException when the database
-            // is not capable of keyset pagination.
+            // is not capable of cursor-based pagination.
             return;
         }
 
@@ -798,7 +798,7 @@ public class EntityTests {
             page = positives.findByFloorOfSquareRootNotAndIdLessThanOrderByBitsRequiredDesc(4L, 33L, page.nextPageRequest());
         } catch (MappingException x) {
             // Test passes: Jakarta Data providers must raise MappingException when the database
-            // is not capable of keyset pagination.
+            // is not capable of cursor-based pagination.
             return;
         }
 
@@ -820,7 +820,7 @@ public class EntityTests {
             slice = numbers.findByFloorOfSquareRootOrderByIdAsc(7L, first6);
         } catch (MappingException x) {
             // Test passes: Jakarta Data providers must raise MappingException when the database
-            // is not capable of keyset pagination.
+            // is not capable of cursor-based pagination.
             return;
         }
 
@@ -833,7 +833,7 @@ public class EntityTests {
             slice = numbers.findByFloorOfSquareRootOrderByIdAsc(7L, slice.nextPageRequest());
         } catch (MappingException x) {
             // Test passes: Jakarta Data providers must raise MappingException when the database
-            // is not capable of keyset pagination.
+            // is not capable of cursor-based pagination.
             return;
         }
 
@@ -846,7 +846,7 @@ public class EntityTests {
             slice = numbers.findByFloorOfSquareRootOrderByIdAsc(7L, slice.nextPageRequest());
         } catch (MappingException x) {
             // Test passes: Jakarta Data providers must raise MappingException when the database
-            // is not capable of keyset pagination.
+            // is not capable of cursor-based pagination.
             return;
         }
 
@@ -982,14 +982,14 @@ public class EntityTests {
 
         PageRequest<NaturalNumber> middle7 = PageRequest.of(NaturalNumber.class).size(7)
                         .sortBy(Sort.desc("numBitsRequired"), Sort.asc("floorOfSquareRoot"), Sort.desc("id"))
-                        .afterKeyset((short) 5, 5L, 26L); // 20th result is 26; it requires 5 bits and its square root rounds down to 5.
+                        .afterKey((short) 5, 5L, 26L); // 20th result is 26; it requires 5 bits and its square root rounds down to 5.
 
         CursoredPage<NaturalNumber> page;
         try {
             page = positives.findByFloorOfSquareRootNotAndIdLessThanOrderByBitsRequiredDesc(6L, 50L, middle7);
         } catch (MappingException x) {
             // Test passes: Jakarta Data providers must raise MappingException when the database
-            // is not capable of keyset pagination.
+            // is not capable of cursor-based pagination.
             return;
         }
 
@@ -1009,7 +1009,7 @@ public class EntityTests {
                                                                                                     page.previousPageRequest());
         } catch (MappingException x) {
             // Test passes: Jakarta Data providers must raise MappingException when the database
-            // is not capable of keyset pagination.
+            // is not capable of cursor-based pagination.
             return;
         }
 
@@ -1026,7 +1026,7 @@ public class EntityTests {
                                                                                                 page.nextPageRequest());
         } catch (MappingException x) {
             // Test passes: Jakarta Data providers must raise MappingException when the database
-            // is not capable of keyset pagination.
+            // is not capable of cursor-based pagination.
             return;
         }
 
@@ -1048,7 +1048,7 @@ public class EntityTests {
             page = positives.findByFloorOfSquareRootNotAndIdLessThanOrderByBitsRequiredDesc(1L, 4L, PageRequest.ofPage(1L));
         } catch (MappingException x) {
             // Test passes: Jakarta Data providers must raise MappingException when the database
-            // is not capable of keyset pagination.
+            // is not capable of cursor-based pagination.
             return;
         }
 
@@ -1094,14 +1094,14 @@ public class EntityTests {
 
         PageRequest<NaturalNumber> middle9 = PageRequest.of(NaturalNumber.class).size(9).withoutTotal()
                              .sortBy(Sort.desc("floorOfSquareRoot"), Sort.asc("id"))
-                             .afterKeyset(6L, 46L); // 20th result is 46; its square root rounds down to 6.
+                             .afterKey(6L, 46L); // 20th result is 46; its square root rounds down to 6.
 
         CursoredPage<NaturalNumber> slice;
         try {
             slice = numbers.findByNumTypeAndNumBitsRequiredLessThan(NumberType.COMPOSITE, (short) 7, middle9);
         } catch (MappingException x) {
             // Test passes: Jakarta Data providers must raise MappingException when the database
-            // is not capable of keyset pagination.
+            // is not capable of cursor-based pagination.
             return;
         }
 
@@ -1118,7 +1118,7 @@ public class EntityTests {
                                                                             slice.previousPageRequest());
         } catch (MappingException x) {
             // Test passes: Jakarta Data providers must raise MappingException when the database
-            // is not capable of keyset pagination.
+            // is not capable of cursor-based pagination.
             return;
         }
 
@@ -1134,7 +1134,7 @@ public class EntityTests {
                                                                          slice.nextPageRequest());
          } catch (MappingException x) {
              // Test passes: Jakarta Data providers must raise MappingException when the database
-             // is not capable of keyset pagination.
+             // is not capable of cursor-based pagination.
              return;
          }
 
@@ -1147,14 +1147,14 @@ public class EntityTests {
     @Assertion(id = "133", strategy = "Request a CursoredPage of results where none match the query, expecting an empty CursoredPage with 0 results.")
     public void testCursoredPageWithoutTotalOfNothing() {
         // There are no numbers larger than 30 which have a square root that rounds down to 3.
-        PageRequest<NaturalNumber> pagination = PageRequest.of(NaturalNumber.class).size(33).afterKeyset(30L).withoutTotal();
+        PageRequest<NaturalNumber> pagination = PageRequest.of(NaturalNumber.class).size(33).afterKey(30L).withoutTotal();
 
         CursoredPage<NaturalNumber> slice;
         try {
             slice = numbers.findByFloorOfSquareRootOrderByIdAsc(3L, pagination);
         } catch (MappingException x) {
             // Test passes: Jakarta Data providers must raise MappingException when the database
-            // is not capable of keyset pagination.
+            // is not capable of cursor-based pagination.
             return;
         }