Repository: jakartaee/data
Pull request #517 (2024-03-01 23:16:06)

simplify pagination API
Implements the solution suggested by @njr-11

It seems to me that this design reduces:

1. understandability from the point of view of the user
2. burden on implementors
3. burden on development and maintenance of the TCK
4. constraints on future evolution of the API

This for a rather small loss of typesafety.

See #504.

gavinking: I have now also pulled the operations for reverse-navigation up to `Page`, since there's no reason why offset-based pagination has to go only in a forward direction.
gavinking: @njr-11 I applied all your suggestions.

Now I will try to resolve the conflicts.

Patch:
diff --git a/api/src/main/java/jakarta/data/Streamable.java b/api/src/main/java/jakarta/data/Streamable.java
deleted file mode 100644
index 7a5e1f2c..00000000
--- a/api/src/main/java/jakarta/data/Streamable.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2022,2023 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.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-package jakarta.data;
-
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-
-/**
- * <p>An interface for sequential streaming of an {@link Iterable}.
- * This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.
- * </p>
- *
- * <p>Data is fetched once per <code>Streamable</code> instance;
- * it is not re-fetched with each invocation of {@link #stream()}
- * and {@link #iterator()}.</p>
- *
- * @param <T> the element type.
- */
-@FunctionalInterface
-public interface Streamable<T> extends Iterable<T> {
-
-    /**
-     * 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);
-    }
-
-
-}
diff --git a/api/src/main/java/jakarta/data/page/KeysetAwareSlice.java b/api/src/main/java/jakarta/data/page/CursoredPage.java
similarity index 79%
rename from api/src/main/java/jakarta/data/page/KeysetAwareSlice.java
rename to api/src/main/java/jakarta/data/page/CursoredPage.java
index 4411ef58..3bb30141 100644
--- a/api/src/main/java/jakarta/data/page/KeysetAwareSlice.java
+++ b/api/src/main/java/jakarta/data/page/CursoredPage.java
@@ -41,15 +41,14 @@ import java.util.NoSuchElementException;
  * 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>
@@ -66,7 +65,7 @@ import java.util.NoSuchElementException;
  * </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>
  *
@@ -114,7 +113,7 @@ import java.util.NoSuchElementException;
  * &#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>
  *
@@ -130,14 +129,14 @@ import java.util.NoSuchElementException;
  *
  * <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.
@@ -149,37 +148,37 @@ public interface KeysetAwareSlice<T> extends Slice<T> {
 
     /**
      * Returns {@code true} when it is possible to navigate to a previous
-     * page of results or if it is necessary to request a previous page in order to
-     * determine whether there are more previous results.
+     * page of results or if it is necessary to request a previous page in
+     * order to determine whether there are more previous results.
      * @return {@code false} if the current page is empty or if it is known
      *         that there is not a previous page.
      */
     boolean hasPrevious();
 
     /**
-     * <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 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 entity.</p>
+     * <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
+     * 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
+     * entity.</p>
      *
      * @return pagination information for requesting the next page.
-     * @throws NoSuchElementException if the current page is empty
-     *         or it is known that there is no next page.
-     *         To avoid this exception, check for a {@code true} result of
-     *         {@link Slice#hasNext()} before invoking this method.
+     * @throws NoSuchElementException if the current page is empty or if
+     *         it is known that there is no next page.
+     *         To avoid this exception, check for a {@code true} result
+     *         of {@link #hasNext()} before invoking this method.
      */
     PageRequest<T> nextPageRequest();
 
     /**
-     * <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 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 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>Creates a request for the previous page in a reverse direction from
+     * the current page. This method computes a keyset 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
+     * 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
@@ -194,10 +193,10 @@ public interface KeysetAwareSlice<T> extends Slice<T> {
      * current page number.</p>
      *
      * @return pagination information for requesting the previous page.
-     * @throws NoSuchElementException if the current page is empty
-     *         or it is known that there is no previous page.
-     *         To avoid this exception, check for a {@code true} result of
-     *         {@link #hasPrevious()} before invoking this method.
+     * @throws NoSuchElementException if the current page is empty or if
+     *         it is known that there is no previous page.
+     *         To avoid this exception, check for a {@code true} result
+     *         of {@link #hasPrevious()} before invoking this method.
      */
     PageRequest<T> previousPageRequest();
 }
\ No newline at end of file
diff --git a/api/src/main/java/jakarta/data/page/KeysetAwarePage.java b/api/src/main/java/jakarta/data/page/KeysetAwarePage.java
deleted file mode 100644
index b49cf097..00000000
--- a/api/src/main/java/jakarta/data/page/KeysetAwarePage.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 2022,2023 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.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *  SPDX-License-Identifier: Apache-2.0
- */
-package jakarta.data.page;
-
-/**
- * <p>A page of data with the ability to create a cursor from the keyset
- * of each entity in the page. This class inherits from {@link KeysetAwareSlice},
- * which is where most of the documentation on keyset curosr-based pagination
- * can be found.</p>
- *
- * <p>Page numbers
- * and total numbers of pages and results cannot be accurately known
- * with keyset pagination, which allows entities to be added and removed
- * in between traversal of slices or pages. When keyset pagination is used,
- * these values should not be relied upon.</p>
- *
- * @param <T> the type of elements in this page 
- */
-public interface KeysetAwarePage<T> extends KeysetAwareSlice<T>, Page<T> {
-}
\ No newline at end of file
diff --git a/api/src/main/java/jakarta/data/page/Page.java b/api/src/main/java/jakarta/data/page/Page.java
index 24625b3d..5d6e08f9 100644
--- a/api/src/main/java/jakarta/data/page/Page.java
+++ b/api/src/main/java/jakarta/data/page/Page.java
@@ -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.
@@ -17,39 +17,193 @@
  */
 package jakarta.data.page;
 
+import jakarta.data.repository.Query;
+import java.util.List;
+import java.util.NoSuchElementException;
+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<Vehicle>} 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 any were 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 page is smaller than the maximum page size,
+     * then there are no subsequent pages 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 {@code true} if it is known that there are previous results or that it
+     * is necessary to request the previous page to determine whether there are previous
+     * results, so that {@link #previousPageRequest()} will not return {@code null}.
+     * @return {@code false} if this is the first page of results.
+     */
+    boolean hasPrevious();
+
+    /**
+     * Returns the {@linkplain PageRequest page request} for which this
+     * page was obtained.
+     *
+     * @return the request for the current page; will never be {@code null}.
+     */
+    PageRequest<T> pageRequest();
+
+    /**
+     * <p>Returns the {@linkplain PageRequest page request} for which this page was
+     * obtained.</p>
+     *
+     * <p>This method is useful when the {@linkplain Query query language} is used
+     * to return a result of different type to the entity class that was queried,
+     * allowing the subsequent {@code PageRequest} to be cast back to 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 {@linkplain PageRequest#next() next} page if
+     * {@link #hasNext()} indicates there might be a next page.
+     *
+     * @return a request for the next page.
+     * @throws NoSuchElementException if it is known that there is no next page.
+     *         To avoid this exception, check for a {@code true} result of
+     *         {@link #hasNext()} before invoking this method.
+     */
+    PageRequest<T> nextPageRequest();
+
+    /**
+     * <p>Returns a request for the {@linkplain PageRequest#next() next} page if
+     * {@link #hasNext()} indicates there might be a next page.</p>
+     *
+     * <p>This method is useful when the {@linkplain Query query language} is used
+     * to return a result of different type to the entity class that was queried,
+     * allowing the subsequent {@code PageRequest} to be cast back to 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 a request for the next page.
+     * @throws NoSuchElementException if it is known that there is no next page.
+     *         To avoid this exception, check for a {@code true} result of
+     *         {@link #hasNext()} before invoking this method.
+     */
+    <E> PageRequest<E> nextPageRequest(Class<E> entityClass);
+
+    /**
+     * <p>Returns a request for the {@link PageRequest#previous() previous} page,
+     * if {@link #hasPrevious()} indicates there might be a previous page.</p>
+     *
+     * @return a request for the previous page.
+     * @throws NoSuchElementException if it is known that there is no previous page.
+     *         To avoid this exception, check for a {@code true} result of
+     *         {@link #hasPrevious()} before invoking this method.
+     */
+    PageRequest<T> previousPageRequest();
+
+    /**
+     * <p>Returns a request for the {@linkplain PageRequest#previous() previous}
+     * page, if {@link #hasPrevious()} indicates there might be a previous page.</p>
+     *
+     * <p>This method is useful when the {@linkplain Query query language} is used
+     * to return a result of different type to the entity class that was queried,
+     * allowing the subsequent {@code PageRequest} to be cast back to 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 a request for the previous page.
+     * @throws NoSuchElementException if it is known that there is no previous page.
+     *         To avoid this exception, check for a {@code true} result of
+     *         {@link #hasPrevious()} before invoking this method.
+     */
+    <E> PageRequest<E> previousPageRequest(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();
-}
\ No newline at end of file
+}
diff --git a/api/src/main/java/jakarta/data/page/PageRequest.java b/api/src/main/java/jakarta/data/page/PageRequest.java
index 26db1621..e61aa7a5 100644
--- a/api/src/main/java/jakarta/data/page/PageRequest.java
+++ b/api/src/main/java/jakarta/data/page/PageRequest.java
@@ -29,11 +29,11 @@ import java.util.List;
 import java.util.Optional;
 
 /**
- * <p>A request for a page or slice.</p>
+ * <p>A request for a single well-specified page of query results.</p>
  *
- * <p><code>PageRequest</code> is optionally specified as a parameter to a
- * repository method in one of the parameter positions after the
- * query parameters. For example,</p>
+ * <p><code>PageRequest</code> is optionally specified as a parameter to
+ * a repository method in one of the parameter positions after the query
+ * parameters. For example,</p>
  *
  * <pre>
  * &#64;OrderBy("age")
@@ -49,21 +49,22 @@ import java.util.Optional;
  * </pre>
  *
  * <p>A repository method will fail with a
- * {@link jakarta.data.exceptions.DataException DataException}
- * or a more specific subclass if</p>
+ * {@link jakarta.data.exceptions.DataException DataException} or a more
+ * specific subclass if:</p>
  * <ul>
- * <li>multiple <code>PageRequest</code> parameters are supplied to the
- *     same method.</li>
- * <li><code>PageRequest</code> and {@link Limit} parameters are supplied to the
- *     same method.</li>
- * <li>a <code>PageRequest</code> parameter is supplied in combination
- *     with the <code>First</code> keyword.</li>
- * <li>a <code>PageRequest</code> parameter with sort criteria is supplied and separate
- *     {@link Sort} parameters are also supplied to the same method.</li>
- * <li>a <code>PageRequest</code> parameter with sort criteria is supplied and an
- *     {@link Order} parameter is also supplied to the same method.</li>
- * <li>the database is incapable of ordering with the requested
- *     sort criteria.</li>
+ * <li>a repository method has more than one parameter of type
+ *     {@code PageRequest}, or has a parameter of type
+ *     {@code PageRequest} and a parameter of type {@link Limit},</li>
+ * <li>a repository method has a parameter of type {@code PageRequest} in
+ *     combination with the keyword {@code First},</li>
+ * <li>a {@code PageRequest} argument with sort criteria is supplied to a
+ *     repository method, and separate {@link Sort} arguments are also
+ *     supplied,</li>
+ * <li>a {@code PageRequest} argument with sort criteria is supplied to a
+ *     repository method, and an {@link Order} argument is also supplied,
+ *     or</li>
+ * <li>the database is incapable of ordering the query results using the
+ *     given sort criteria.</li>
  * </ul>
  *
  * @param <T> entity class of the attributes that are used as sort criteria.
@@ -71,10 +72,11 @@ import java.util.Optional;
 public interface PageRequest<T> {
 
     /**
-     * <p>Creates a page request to use when querying on entities of the specified entity class.</p>
+     * <p>Creates a page request to use when querying on entities of the
+     * specified entity class.</p>
      *
-     * <p>This method is useful for supplying the entity type when you do not have
-     * typed {@link Sort} instances. For example,</p>
+     * <p>This method is useful for supplying the entity type when you
+     * do not have typed {@link Sort} instances. For example,</p>
      *
      * <pre>
      * {@code PageRequest<Car>} page1Request = PageRequest.of(Car.class).page(1).size(25).sortBy(
@@ -116,8 +118,8 @@ public interface PageRequest<T> {
     }
 
     /**
-     * Creates a new page request for requesting pages of the
-     * specified size, starting with the first page number, which is 1.
+     * Creates a new page request for requesting pages of the specified size,
+     * starting with the first page number, which is 1.
      *
      * @param <T>         entity class of the attributes that are used as sort criteria.
      * @param maxPageSize The number of query results in a full page.
@@ -129,7 +131,7 @@ public interface PageRequest<T> {
     }
 
     /**
-     * <p>Requests {@link KeysetAwareSlice keyset pagination} in the forward direction,
+     * <p>Requests {@link CursoredPage keyset pagination} in the forward direction,
      * starting after the specified keyset values.</p>
      *
      * @param keyset keyset values, the order and number of which must match the
@@ -143,7 +145,7 @@ public interface PageRequest<T> {
     PageRequest<T> afterKeyset(Object... keyset);
 
     /**
-     * <p>Requests {@link KeysetAwareSlice keyset pagination} in the reverse direction,
+     * <p>Requests {@link CursoredPage keyset pagination} in the reverse direction,
      * starting after the specified keyset values.</p>
      *
      * @param keyset keyset values, the order and number of which must match the
@@ -157,7 +159,7 @@ public interface PageRequest<T> {
     PageRequest<T> beforeKeyset(Object... keyset);
 
     /**
-     * <p>Requests {@link KeysetAwareSlice keyset pagination} in the forward direction,
+     * <p>Requests {@link CursoredPage keyset pagination} in the forward direction,
      * starting after the specified keyset values.</p>
      *
      * @param keysetCursor cursor with keyset values, the order and number of which must match the
@@ -171,7 +173,7 @@ public interface PageRequest<T> {
     PageRequest<T> afterKeysetCursor(Cursor keysetCursor);
 
     /**
-     * <p>Requests {@link KeysetAwareSlice keyset pagination} in the reverse direction,
+     * <p>Requests {@link CursoredPage keyset pagination} in the reverse direction,
      * starting after the specified keyset values.</p>
      *
      * @param keysetCursor cursor with keyset values, the order and number of which must match the
@@ -278,146 +280,205 @@ public interface PageRequest<T> {
     int size();
 
     /**
-     * Return the order collection if it was specified on this page request, otherwise an empty list.
+     * Indicates that a query method which returns a {@link Page}
+     * should retrieve the {@linkplain Page#totalElements() total
+     * number elements} available across all pages. This behavior
+     * is enabled by default. To obtain a page request with total
+     * retrieval disabled, call {@link #withoutTotal()}.
+     * @return {@code true} if the total number of elements should
+     *         be retrieved from the database.
+     */
+    boolean requestTotal();
+
+    /**
+     * Return the order collection if it was specified on this page request,
+     * otherwise an empty list.
      *
      * @return the order collection; will never be {@code null}.
      */
     List<Sort<? super T>> sorts();
 
     /**
-     * <p>Returns the <code>PageRequest</code> requesting the next page
-     * if using offset pagination.</p>
+     * <p>Returns the <code>PageRequest</code> requesting the next page if
+     * using offset pagination.</p>
      *
      * <p>If using keyset pagination, traversal of pages must only be done
-     * via the {@link KeysetAwareSlice#nextPageRequest()},
-     * {@link KeysetAwareSlice#previousPageRequest()}, or
-     * {@link KeysetAwareSlice#getKeysetCursor(int) keyset cursor},
+     * via the {@link CursoredPage#nextPageRequest()},
+     * {@link CursoredPage#previousPageRequest()}, or
+     * {@linkplain CursoredPage#getKeysetCursor(int) keyset cursor},
      * not with this method.</p>
      *
      * @return The next PageRequest.
-     * @throws UnsupportedOperationException if this <code>PageRequest</code> has a
-     *         {@link PageRequest.Cursor Cursor}.
+     * @throws UnsupportedOperationException if this {@code PageRequest}
+     *         has a {@link PageRequest.Cursor Cursor}.
      */
     PageRequest<T> next();
 
     /**
-     * <p>Creates a new page request with the same
-     * pagination information, except with the specified page number.</p>
+     * <p>Returns the <code>PageRequest</code> requesting the previous page
+     * 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
+     * via the {@link CursoredPage#nextPageRequest()},
+     * {@link CursoredPage#previousPageRequest()}, or
+     * {@linkplain CursoredPage#getKeysetCursor(int) keyset cursor},
+     * not with this method.</p>
+     *
+     * @return The previous PageRequest, or null if this is the first page.
+     * @throws UnsupportedOperationException if this {@code PageRequest}
+     *         has a {@link PageRequest.Cursor Cursor}.
+     */
+    PageRequest<T> previous();
+
+    /**
+     * <p>Creates a new page request with the same pagination information,
+     * but with the specified page number.</p>
      *
      * @param pageNumber The page number
-     * @return a new instance of <code>PageRequest</code>. This method never returns <code>null</code>.
+     * @return a new instance of <code>PageRequest</code>.
+     *         This method never returns <code>null</code>.
      */
     PageRequest<T> page(long pageNumber);
 
     /**
-     * <p>Creates a new page request with the same
-     * pagination information, except with the specified maximum page size.
-     * When a page is retrieved from the database, the number of elements in the page
-     * must be equal to the maximum page size unless there is an insufficient number
-     * of elements to retrieve from the database from the start of the page.</p>
+     * <p>Creates a new page request with the same pagination information,
+     * but with the specified maximum page size. When a page is retrieved
+     * from the database, the number of elements in the page must be equal
+     * to the maximum page size unless there is an insufficient number of
+     * elements to retrieve from the database from the start of the page.</p>
      *
      * @param maxPageSize the number of query results in a full page.
-     * @return a new instance of <code>PageRequest</code>. This method never returns <code>null</code>.
+     * @return a new instance of <code>PageRequest</code>.
+     *         This method never returns <code>null</code>.
      */
     PageRequest<T> size(int maxPageSize);
 
     /**
-     * <p>Creates a new page request with the same
-     * pagination information, except using the specified sort criteria.
-     * The order of precedence for sort criteria is that of any statically
-     * specified sort criteria (from the <code>OrderBy</code> keyword,
-     * {@link OrderBy} annotation or <code>ORDER BY</code> clause of a the
-     * {@link Query} annotation) followed by the order of the
-     * {@link Iterable} that is supplied to this method.</p>
+     * <p>Creates a new page request with the same pagination information,
+     * but using the specified sort criteria. The order of precedence for
+     * sort criteria is that of any statically specified sort criteria
+     * (from the <code>OrderBy</code> keyword, {@link OrderBy} annotation,
+     * or <code>ORDER BY</code> clause of a the {@link Query} annotation),
+     * followed by the order of the {@link Iterable} that is supplied to
+     * this method.</p>
      *
      * @param sorts sort criteria to use.
-     * @return a new instance of <code>PageRequest</code>. This method never returns <code>null</code>.
+     * @return a new instance of <code>PageRequest</code>.
+     *         This method never returns <code>null</code>.
      */
     PageRequest<T> sortBy(Iterable<Sort<? super T>> sorts);
 
     /**
-     * <p>Creates a new page request with the same
-     * pagination information, except using the specified sort criteria.
-     * The order of precedence for sort criteria is that of any statically
-     * specified sort criteria (from the <code>OrderBy</code> keyword,
-     * {@link OrderBy} annotation or <code>ORDER BY</code> clause of a the
-     * {@link Query} annotation) followed by the order in which the
-     * {@link Sort} parameters to this method are listed.</p>
+     * <p>Creates a new page request with the same pagination information,
+     * but using the specified sort criteria. The order of precedence for
+     * sort criteria is that of any statically specified sort criteria
+     * (from the <code>OrderBy</code> keyword, {@link OrderBy} annotation,
+     * or <code>ORDER BY</code> clause of a the {@link Query} annotation),
+     * followed by the order in which the {@link Sort} parameters to this
+     * method are listed.</p>
      *
      * @param sort sort criteria to use.
-     * @return a new instance of <code>PageRequest</code>. This method never returns <code>null</code>.
+     * @return a new instance of <code>PageRequest</code>. T
+     *         his method never returns <code>null</code>.
      */
     PageRequest<T> sortBy(Sort<? super T> sort);
 
     /**
-     * <p>Creates a new page request with the same
-     * pagination information, except using the specified sort criteria.
-     * The order of precedence for sort criteria is that of any statically
-     * specified sort criteria (from the <code>OrderBy</code> keyword,
-     * {@link OrderBy} annotation or <code>ORDER BY</code> clause of a the
-     * {@link Query} annotation) followed by the order in which the
-     * {@link Sort} parameters to this method are listed.</p>
+     * <p>Creates a new page request with the same pagination information,
+     * but using the specified sort criteria. The order of precedence for
+     * sort criteria is that of any statically specified sort criteria
+     * (from the <code>OrderBy</code> keyword, {@link OrderBy} annotation,
+     * or <code>ORDER BY</code> clause of a the {@link Query} annotation),
+     * followed by the order in which the {@link Sort} parameters to this
+     * method are listed.</p>
      *
      * @param sort1 dynamic sort criteria to use first.
      * @param sort2 dynamic sort criteria to use second.
-     * @return a new instance of <code>PageRequest</code>. This method never returns <code>null</code>.
+     * @return a new instance of <code>PageRequest</code>.
+     *         This method never returns <code>null</code>.
      */
     PageRequest<T> sortBy(Sort<? super T> sort1, Sort<? super T> sort2);
 
     /**
-     * <p>Creates a new page request with the same
-     * pagination information, except using the specified sort criteria.
-     * The order of precedence for sort criteria is that of any statically
-     * specified sort criteria (from the <code>OrderBy</code> keyword,
-     * {@link OrderBy} annotation or <code>ORDER BY</code> clause of a the
-     * {@link Query} annotation) followed by the order in which the
-     * {@link Sort} parameters to this method are listed.</p>
+     * <p>Creates a new page request with the same pagination information,
+     * but using the specified sort criteria. The order of precedence for
+     * sort criteria is that of any statically specified sort criteria
+     * (from the <code>OrderBy</code> keyword, {@link OrderBy} annotation,
+     * or <code>ORDER BY</code> clause of a the {@link Query} annotation),
+     * followed by the order in which the {@link Sort} parameters to this
+     * method are listed.</p>
      *
      * @param sort1 dynamic sort criteria to use first.
      * @param sort2 dynamic sort criteria to use second.
      * @param sort3 dynamic sort criteria to use last.
-     * @return a new instance of <code>PageRequest</code>. This method never returns <code>null</code>.
+     * @return a new instance of <code>PageRequest</code>.
+     *         This method never returns <code>null</code>.
      */
     PageRequest<T> sortBy(Sort<? super T> sort1, Sort<? super T> sort2, Sort<? super T> sort3);
 
     /**
-     * <p>Creates a new page request with the same
-     * pagination information, except using the specified sort criteria.
-     * The order of precedence for sort criteria is that of any statically
-     * specified sort criteria (from the <code>OrderBy</code> keyword,
-     * {@link OrderBy} annotation or <code>ORDER BY</code> clause of a the
-     * {@link Query} annotation) followed by the order in which the
-     * {@link Sort} parameters to this method are listed.</p>
+     * <p>Creates a new page request with the same pagination information,
+     * but using the specified sort criteria. The order of precedence for
+     * sort criteria is that of any statically specified sort criteria
+     * (from the <code>OrderBy</code> keyword, {@link OrderBy} annotation,
+     * or <code>ORDER BY</code> clause of a the {@link Query} annotation),
+     * followed by the order in which the {@link Sort} parameters to this
+     * method are listed.</p>
      *
      * @param sort1 dynamic sort criteria to use first.
      * @param sort2 dynamic sort criteria to use second.
      * @param sort3 dynamic sort criteria to use third.
      * @param sort4 dynamic sort criteria to use last.
-     * @return a new instance of <code>PageRequest</code>. This method never returns <code>null</code>.
+     * @return a new instance of <code>PageRequest</code>.
+     *         This method never returns <code>null</code>.
      */
     PageRequest<T> sortBy(Sort<? super T> sort1, Sort<? super T> sort2, Sort<? super T> sort3, Sort<? super T> sort4);
 
     /**
-     * <p>Creates a new page request with the same
-     * pagination information, except using the specified sort criteria.
-     * The order of precedence for sort criteria is that of any statically
-     * specified sort criteria (from the <code>OrderBy</code> keyword,
-     * {@link OrderBy} annotation or <code>ORDER BY</code> clause of a the
-     * {@link Query} annotation) followed by the order in which the
-     * {@link Sort} parameters to this method are listed.</p>
+     * <p>Creates a new page request with the same pagination information,
+     * but using the specified sort criteria. The order of precedence for
+     * sort criteria is that of any statically specified sort criteria
+     * (from the <code>OrderBy</code> keyword, {@link OrderBy} annotation,
+     * or <code>ORDER BY</code> clause of a the {@link Query} annotation),
+     * followed by the order in which the {@link Sort} parameters to this
+     * method are listed.</p>
      *
      * @param sort1 dynamic sort criteria to use first.
      * @param sort2 dynamic sort criteria to use second.
      * @param sort3 dynamic sort criteria to use third.
      * @param sort4 dynamic sort criteria to use fourth.
      * @param sort5 dynamic sort criteria to use last.
-     * @return a new instance of <code>PageRequest</code>. This method never returns <code>null</code>.
+     * @return a new instance of <code>PageRequest</code>.
+     *         This method never returns <code>null</code>.
      */
     PageRequest<T> sortBy(Sort<? super T> sort1, Sort<? super T> sort2, Sort<? super T> sort3, Sort<? super T> sort4, Sort<? super T> sort5);
 
     /**
-     * The type of pagination: offset-based or
-     * keyset cursor-based, which includes a direction.
+     * Returns an otherwise-equivalent page request with
+     * {@link #requestTotal()} set to {@code false}, so that
+     * totals will not be retrieved from the database. Note
+     * that when totals are not retrieved by a repository
+     * method with return type {@link Page}, the operations
+     * {@link Page#totalElements()} and {@link Page#totalPages()}
+     * throw an {@link IllegalStateException} when called.
+     * @return a page request with {@link #requestTotal()}
+     *         set to {@code false}.
+     */
+    PageRequest<T> withoutTotal();
+
+    /**
+     * Returns an otherwise-equivalent page request with
+     * {@link #requestTotal()} set to {@code false}, so that
+     * totals will be retrieved from the database.
+     * @return a page request with {@link #requestTotal()}
+     *         set to {@code true}.
+     */
+    PageRequest<T> withTotal();
+
+    /**
+     * The type of pagination: offset-based or keyset cursor-based,
+     * which includes a direction.
      */
     enum Mode {
         /**
@@ -510,7 +571,7 @@ public interface PageRequest<T> {
          * @return a new instance of {@code Cursor}
          */
         static Cursor forKeyset(Object... keyset) {
-            return new KeysetCursor(keyset);
+            return new PageRequestCursor(keyset);
         }
     }
 }
\ No newline at end of file
diff --git a/api/src/main/java/jakarta/data/page/KeysetCursor.java b/api/src/main/java/jakarta/data/page/PageRequestCursor.java
similarity index 92%
rename from api/src/main/java/jakarta/data/page/KeysetCursor.java
rename to api/src/main/java/jakarta/data/page/PageRequestCursor.java
index 11f2edc3..db476170 100644
--- a/api/src/main/java/jakarta/data/page/KeysetCursor.java
+++ b/api/src/main/java/jakarta/data/page/PageRequestCursor.java
@@ -23,7 +23,7 @@ import java.util.List;
 /**
  * Built-in implementation of Cursor for keyset pagination.
  */
-class KeysetCursor implements PageRequest.Cursor {
+class PageRequestCursor implements PageRequest.Cursor {
     /**
      * Keyset values.
      */
@@ -35,7 +35,7 @@ class KeysetCursor implements PageRequest.Cursor {
      * @param keyset keyset values.
      * @throws IllegalArgumentException if no keyset values are provided.
      */
-    KeysetCursor(Object... keyset) {
+    PageRequestCursor(Object... keyset) {
         this.keyset = keyset;
         if (keyset == null || keyset.length == 0)
             throw new IllegalArgumentException("No keyset values were provided.");
@@ -45,7 +45,7 @@ class KeysetCursor implements PageRequest.Cursor {
     public boolean equals(Object o) {
         return this == o || o != null
                 && o.getClass() == getClass()
-                && Arrays.equals(keyset, ((KeysetCursor) o).keyset);
+                && Arrays.equals(keyset, ((PageRequestCursor) o).keyset);
     }
 
     public Object getKeysetElement(int index) {
diff --git a/api/src/main/java/jakarta/data/page/Pagination.java b/api/src/main/java/jakarta/data/page/Pagination.java
index e6a0c1fb..dac99ab8 100644
--- a/api/src/main/java/jakarta/data/page/Pagination.java
+++ b/api/src/main/java/jakarta/data/page/Pagination.java
@@ -29,7 +29,11 @@ import java.util.stream.StreamSupport;
 /**
  * Built-in implementation of PageRequest.
  */
-record Pagination<T>(long page, int size, List<Sort<? super T>> sorts, Mode mode, Cursor type) implements PageRequest<T> {
+record Pagination<T>(long page, int size, List<Sort<? super T>> sorts, Mode mode, Cursor type, boolean requestTotal) implements PageRequest<T> {
+
+    Pagination(long page, int size, List<Sort<? super T>> sorts, Mode mode, Cursor type) {
+        this(page, size, sorts, mode, type, true);
+    }
 
     Pagination {
         if (page < 1) {
@@ -43,14 +47,24 @@ record Pagination<T>(long page, int size, List<Sort<? super T>> sorts, Mode mode
         }
     }
 
+    @Override
+    public PageRequest<T> withoutTotal() {
+        return new Pagination<>(page, size, sorts, mode, type, false);
+    }
+
+    @Override
+    public PageRequest<T> withTotal() {
+        return new Pagination<>(page, size, sorts, mode, type, true);
+    }
+
     @Override
     public PageRequest<T> afterKeyset(Object... keyset) {
-        return new Pagination<T>(page, size, sorts, Mode.CURSOR_NEXT, new KeysetCursor(keyset));
+        return new Pagination<T>(page, size, sorts, Mode.CURSOR_NEXT, new PageRequestCursor(keyset));
     }
 
     @Override
     public PageRequest<T> beforeKeyset(Object... keyset) {
-        return new Pagination<T>(page, size, sorts, Mode.CURSOR_PREVIOUS, new KeysetCursor(keyset));
+        return new Pagination<T>(page, size, sorts, Mode.CURSOR_PREVIOUS, new PageRequestCursor(keyset));
     }
 
     @Override
@@ -76,7 +90,7 @@ record Pagination<T>(long page, int size, List<Sort<? super T>> sorts, Mode mode
     private static final <E> List<E> combine(List<E> list, E element) {
         int size = list.size();
         if (size == 0) {
-            return List.of(element);
+            return java.util.List.of(element);
         } else {
             Object[] array = list.toArray(new Object[size + 1]);
             array[size] = element;
@@ -104,10 +118,20 @@ record Pagination<T>(long page, int size, List<Sort<? super T>> sorts, Mode mode
     @Override
     public PageRequest<T> next() {
         if (mode == Mode.OFFSET) {
-            return new Pagination<T>((page + 1), this.size, this.sorts, Mode.OFFSET, null);
+            return new Pagination<T>(page + 1, this.size, this.sorts, Mode.OFFSET, null);
         } 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 KeysetAwareSlice.");
+                    "to provide the next keyset values or obtain the nextPageRequest from a CursoredPage.");
+        }
+    }
+
+    @Override
+    public PageRequest<T> previous() {
+        if (mode == Mode.OFFSET) {
+            return page()<=1 ? null : new Pagination<T>(page - 1, this.size, this.sorts, Mode.OFFSET, null);
+        } 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.");
         }
     }
 
diff --git a/api/src/main/java/jakarta/data/page/Slice.java b/api/src/main/java/jakarta/data/page/Slice.java
deleted file mode 100644
index 82c5b8e8..00000000
--- a/api/src/main/java/jakarta/data/page/Slice.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * 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.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * SPDX-License-Identifier: Apache-2.0
- */
-package jakarta.data.page;
-
-import jakarta.data.Streamable;
-import jakarta.data.repository.Query;
-import java.util.List;
-import java.util.NoSuchElementException;
-
-/**
- * <p>A slice contains the data that is retrieved for a page request.
- * A slice is obtained by supplying a {@link PageRequest} parameter to a repository method.
- * For example,</p>
- *
- * <pre>
- * {@code @Find}
- * {@code Slice<Vehicle>} search({@code @By("make")} String make,
- *                       {@code @By("model")} String model,
- *                       {@code @By("year")} int designYear,
- *                       {@code PageRequest<?>} pageRequest);
- * </pre>
- *
- * <p>Unlike {@link Page}, a {@code Slice} does not have awareness of the total number of pages
- * that can be retrieved for the query.</p>
- *
- * <p>The lowercase terms, <i>slice</i> and <i>page</i>, are used interchangeably
- * throughout this specification. Wherever a distinction needs to be made between them,
- * the class name, {@code Slice} or {@code Page}, is used.</p>
- *
- * @param <T> the type of elements in this slice.
- */
-public interface Slice<T> extends Streamable<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 Slice} has content at all.
-     *
-     * @return whether the {@link Slice} has content at all.
-     */
-    boolean hasContent();
-
-    /**
-     * Returns the number of elements in this Slice,
-     * 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 in this Slice.
-     */
-    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 if
-     * {@link Slice#hasNext()} indicates there might be a next page.
-     *
-     * @return a request for the next page.
-     * @throws NoSuchElementException if it is known that there is no next page.
-     *         To avoid this exception, check for a {@code true} result of
-     *         {@link #hasNext()} before invoking this method.
-     */
-    PageRequest<T> nextPageRequest();
-
-    /**
-     * <p>Returns a request for the {@link PageRequest#next() next} page if
-     * {@link Slice#hasNext()} indicates there might be a 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.
-     * @throws NoSuchElementException if it is known that there is no next page.
-     *         To avoid this exception, check for a {@code true} result of
-     *         {@link #hasNext()} before invoking this method.
-     */
-    <E> PageRequest<E> nextPageRequest(Class<E> entityClass);
-}
diff --git a/api/src/main/java/jakarta/data/page/impl/KeysetAwarePageRecord.java b/api/src/main/java/jakarta/data/page/impl/CursoredPageRecord.java
similarity index 71%
rename from api/src/main/java/jakarta/data/page/impl/KeysetAwarePageRecord.java
rename to api/src/main/java/jakarta/data/page/impl/CursoredPageRecord.java
index 126cce57..7bd5ccfc 100644
--- a/api/src/main/java/jakarta/data/page/impl/KeysetAwarePageRecord.java
+++ b/api/src/main/java/jakarta/data/page/impl/CursoredPageRecord.java
@@ -17,14 +17,15 @@
  */
 package jakarta.data.page.impl;
 
-import jakarta.data.page.KeysetAwarePage;
+import jakarta.data.page.CursoredPage;
 import jakarta.data.page.PageRequest;
 
 import java.util.Iterator;
 import java.util.List;
+import java.util.NoSuchElementException;
 
 /**
- * Record type implementing {@link KeysetAwarePage}.
+ * Record type implementing {@link CursoredPage}.
  * This may be used to simplify implementation of a repository interface.
  *
  * @param content The page content, that is, the query results, in order
@@ -40,10 +41,10 @@ import java.util.List;
  *                            previous page of results
  * @param <T> The type of elements on the page
  */
-public record KeysetAwarePageRecord<T>
+public record CursoredPageRecord<T>
         (List<T> content, List<PageRequest.Cursor> cursors, long totalElements, PageRequest<T> pageRequest,
          PageRequest<T> nextPageRequest, PageRequest<T> previousPageRequest)
-        implements KeysetAwarePage<T> {
+        implements CursoredPage<T> {
 
     @Override
     public boolean hasContent() {
@@ -71,10 +72,30 @@ public record KeysetAwarePageRecord<T>
         return (PageRequest<E>) pageRequest;
     }
 
+    @Override
+    public PageRequest<T> nextPageRequest() {
+        if (nextPageRequest == null)
+            throw new NoSuchElementException();
+        return nextPageRequest;
+    }
+
     @Override
     @SuppressWarnings("unchecked")
     public <E> PageRequest<E> nextPageRequest(Class<E> entityClass) {
-        return (PageRequest<E>) nextPageRequest;
+        return (PageRequest<E>) nextPageRequest();
+    }
+
+    @Override
+    public PageRequest<T> previousPageRequest() {
+        if (previousPageRequest == null)
+            throw new NoSuchElementException();
+        return previousPageRequest;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <E> PageRequest<E> previousPageRequest(Class<E> entityClass) {
+        return (PageRequest<E>) previousPageRequest();
     }
 
     @Override
@@ -87,13 +108,24 @@ public record KeysetAwarePageRecord<T>
         return cursors.get(index);
     }
 
+    @Override
+    public boolean hasTotals() {
+        return totalElements >= 0;
+    }
+
     @Override
     public long totalElements() {
+        if (totalElements<0) {
+            throw new IllegalStateException("total elements are not available");
+        }
         return totalElements;
     }
 
     @Override
     public long totalPages() {
+        if (totalElements<0) {
+            throw new IllegalStateException("total elements are not available");
+        }
         int size = pageRequest.size();
         return (totalElements + size - 1) / size;
     }
diff --git a/api/src/main/java/jakarta/data/page/impl/KeysetAwareSliceRecord.java b/api/src/main/java/jakarta/data/page/impl/KeysetAwareSliceRecord.java
deleted file mode 100644
index c2fbcc5b..00000000
--- a/api/src/main/java/jakarta/data/page/impl/KeysetAwareSliceRecord.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (c) 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.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *  SPDX-License-Identifier: Apache-2.0
- */
-package jakarta.data.page.impl;
-
-import jakarta.data.page.KeysetAwareSlice;
-import jakarta.data.page.PageRequest;
-import jakarta.data.page.PageRequest.Cursor;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
-
-/**
- * Record type implementing {@link KeysetAwareSlice}.
- * This may be used to simplify implementation of a repository interface.
- *
- * @param content The page content, that is, the query results, in order
- * @param cursors A list of {@link Cursor} instances for result, in order
- * @param pageRequest The {@link PageRequest page request} for which this
- *                    slice was obtained
- * @param nextPageRequest A {@link PageRequest page request} for the next
- *                        page of results
- * @param previousPageRequest A {@link PageRequest page request} for the
- *                            previous page of results
- * @param <T> The type of elements on the page
- */
-public record KeysetAwareSliceRecord<T>
-        (List<T> content, List<Cursor> cursors, PageRequest<T> pageRequest,
-         PageRequest<T> nextPageRequest, PageRequest<T> previousPageRequest)
-        implements KeysetAwareSlice<T> {
-
-    @Override
-    public boolean hasContent() {
-        return !content.isEmpty();
-    }
-
-    @Override
-    public int numberOfElements() {
-        return content.size();
-    }
-
-    @Override
-    public boolean hasNext() {
-        return nextPageRequest != null;
-    }
-
-    @Override
-    public boolean hasPrevious() {
-        return previousPageRequest != null;
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public <E> PageRequest<E> pageRequest(Class<E> entityClass) {
-        return (PageRequest<E>) pageRequest;
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public <E> PageRequest<E> nextPageRequest(Class<E> entityClass) {
-        if (nextPageRequest == null)
-            throw new NoSuchElementException();
-        return (PageRequest<E>) nextPageRequest;
-    }
-
-    @Override
-    public PageRequest<T> previousPageRequest() {
-        if (previousPageRequest == null)
-            throw new NoSuchElementException();
-        return previousPageRequest;
-    }
-
-    @Override
-    public Iterator<T> iterator() {
-        return content.iterator();
-    }
-
-    @Override
-    public Cursor getKeysetCursor(int index) {
-        return cursors.get(index);
-    }
-}
diff --git a/api/src/main/java/jakarta/data/page/impl/PageRecord.java b/api/src/main/java/jakarta/data/page/impl/PageRecord.java
index 495da4ad..89e84e9f 100644
--- a/api/src/main/java/jakarta/data/page/impl/PageRecord.java
+++ b/api/src/main/java/jakarta/data/page/impl/PageRecord.java
@@ -19,14 +19,13 @@ package jakarta.data.page.impl;
 
 import jakarta.data.page.Page;
 import jakarta.data.page.PageRequest;
-import jakarta.data.page.Slice;
 
 import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
 
 /**
- * Record type implementing {@link Slice}.
+ * Record type implementing {@link Page}.
  * This may be used to simplify implementation of a repository interface.
  *
  * @param pageRequest The {@link PageRequest page request} for which this
@@ -69,11 +68,27 @@ public record PageRecord<T>(PageRequest<T> pageRequest, List<T> content, long to
 
     @Override
     public PageRequest<T> nextPageRequest() {
-        if (moreResults) {
-            return pageRequest.next();
-        } else {
+        if ( !hasNext() )
             throw new NoSuchElementException();
-        }
+        return pageRequest.next();
+    }
+
+    @Override
+    public boolean hasPrevious() {
+        return pageRequest.page() > 1;
+    }
+
+    @Override
+    public PageRequest<T> previousPageRequest() {
+        if ( !hasPrevious() )
+            throw new NoSuchElementException();
+        return pageRequest.previous();
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <E> PageRequest<E> previousPageRequest(Class<E> entityClass) {
+        return (PageRequest<E>) previousPageRequest();
     }
 
     @Override
@@ -87,8 +102,24 @@ public record PageRecord<T>(PageRequest<T> pageRequest, List<T> content, long to
         return content.iterator();
     }
 
+    @Override
+    public boolean hasTotals() {
+        return totalElements >= 0;
+    }
+
+    @Override
+    public long totalElements() {
+        if (totalElements<0) {
+            throw new IllegalStateException("total elements are not available");
+        }
+        return totalElements;
+    }
+
     @Override
     public long totalPages() {
+        if (totalElements<0) {
+            throw new IllegalStateException("total elements are not available");
+        }
         int size = pageRequest.size();
         return (totalElements + size - 1) / size;
     }
diff --git a/api/src/main/java/jakarta/data/page/impl/SliceRecord.java b/api/src/main/java/jakarta/data/page/impl/SliceRecord.java
deleted file mode 100644
index 3fac247c..00000000
--- a/api/src/main/java/jakarta/data/page/impl/SliceRecord.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (c) 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.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *  SPDX-License-Identifier: Apache-2.0
- */
-package jakarta.data.page.impl;
-
-import jakarta.data.page.PageRequest;
-import jakarta.data.page.Slice;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.NoSuchElementException;
-
-/**
- * Record type implementing {@link Slice}.
- * This may be used to simplify implementation of a repository interface.
- *
- * @param pageRequest The {@link PageRequest page request} for which this
- *                    slice was obtained
- * @param content The page content
- * @param <T> The type of elements on the page
- */
-public record SliceRecord<T>(PageRequest<T> pageRequest, List<T> content, boolean moreResults)
-        implements Slice<T> {
-
-    public SliceRecord(PageRequest<T> pageRequest, List<T> content) {
-        this( pageRequest, content, content.size() == pageRequest.size() );
-    }
-
-    @Override
-    public boolean hasContent() {
-        return !content.isEmpty();
-    }
-
-    @Override
-    public int numberOfElements() {
-        return content.size();
-    }
-
-    @Override
-    public boolean hasNext() {
-        return moreResults;
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public <E> PageRequest<E> pageRequest(Class<E> entityClass) {
-        return (PageRequest<E>) pageRequest;
-    }
-
-    @Override
-    public PageRequest<T> nextPageRequest() {
-        if (moreResults) {
-            return pageRequest.next();
-        } else {
-            throw new NoSuchElementException();
-        }
-    }
-
-    @Override
-    @SuppressWarnings("unchecked")
-    public <E> PageRequest<E> nextPageRequest(Class<E> entityClass) {
-        return (PageRequest<E>) nextPageRequest();
-    }
-
-    @Override
-    public Iterator<T> iterator() {
-        return content.iterator();
-    }
-}
diff --git a/api/src/main/java/jakarta/data/page/package-info.java b/api/src/main/java/jakarta/data/page/package-info.java
index e3e3cdb5..92dcdb27 100644
--- a/api/src/main/java/jakarta/data/page/package-info.java
+++ b/api/src/main/java/jakarta/data/page/package-info.java
@@ -44,10 +44,10 @@
  * but does not offer a total count of results across all slices. Using slice
  * rather than page is more efficient when a total count of results is not needed.</p>
  *
- * <p>Slices and pages can be determined based on fixed positional offset
- * or relative to a cursor.</p>
+ * <p>Pages can be determined based on fixed positional offset or relative to a
+ * cursor.</p>
  *
- * <p>{@link jakarta.data.page.Slice} and {@link jakarta.data.page.Page} are computed based on their positional offset
+ * <p>The elements of a {@link jakarta.data.page.Page} are computed based on their positional offset
  * within the list of results. For example, if you are on the first page of 10
  * and request the next page, the database identifies the matching entities
  * for the query at positions 11 through 20 and retrieves them. Results are
@@ -61,9 +61,9 @@
  * <p>For situations where the above must be avoided or the application wishes to
  * avoid the performance cost of the database re-scanning data from previous pages
  * (to determine the position), Jakarta Data offers a cursor based approach with
- * {@link jakarta.data.page.KeysetAwareSlice} and {@link jakarta.data.page.KeysetAwarePage}. In this approach,
- * queries for the next or previous page are performed relative to the last
- * or first entry of the current page.</p>
+ * {@link jakarta.data.page.CursoredPage}. In this approach, queries for the next
+ * or previous page are performed relative to the last or first entry of the current
+ * page.</p>
  *
  * <p>The module JavaDoc provides an {@link jakarta.data/ overview} of Jakarta Data.</p>
  */
diff --git a/api/src/main/java/jakarta/data/repository/Find.java b/api/src/main/java/jakarta/data/repository/Find.java
index 1c79e32e..58759e32 100644
--- a/api/src/main/java/jakarta/data/repository/Find.java
+++ b/api/src/main/java/jakarta/data/repository/Find.java
@@ -53,7 +53,7 @@ import java.lang.annotation.Target;
  *     <li>an entity array type {@code E[]},
  *     <li>{@code List<E>},</li>
  *     <li>{@code Stream<E>}, or</li>
- *     <li>{@code Page<E>}, {@code Slice<E>}, {@code KeysetAwarePage<E>}, or {@code KeysetAwareSlice<E>}.</li>
+ *     <li>{@code Page<E>} or {@code CursoredPage<E>}.</li>
  * </ul>
  *
  * <p>If the return type of the annotated method is {@code E} or {@code Optional<E>}, and the query returns more than
diff --git a/api/src/main/java/jakarta/data/repository/Query.java b/api/src/main/java/jakarta/data/repository/Query.java
index 3fe75293..7f242de8 100644
--- a/api/src/main/java/jakarta/data/repository/Query.java
+++ b/api/src/main/java/jakarta/data/repository/Query.java
@@ -18,9 +18,9 @@
 package jakarta.data.repository;
 
 import jakarta.data.Sort;
-import jakarta.data.page.KeysetAwarePage;
+import jakarta.data.page.CursoredPage;
 import jakarta.data.page.Page;
-import jakarta.data.page.Slice;
+
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -76,7 +76,7 @@ import java.lang.annotation.Target;
  * <p>Some query languages such as JPQL can be used to return a type other than the
  * entity class, as shown in the above example, resulting in a {@link Page} that is
  * parameterized with the query result type rather than the entity class.
- * to request a subsequent page, use the {@link Slice#nextPageRequest(Class)} method
+ * to request a subsequent page, use the {@link Page#nextPageRequest(Class)} method
  * to specify the entity class. For example,</p>
  *
  * <pre>
@@ -108,11 +108,9 @@ public @interface Query {
     /**
      * <p>Defines an additional query that counts the number of elements that are
      * returned by the {@link #value() primary} query. This is used to compute
-     * the {@link Page#totalElements() total elements}
-     * and {@link Page#totalPages() total pages}
-     * for paginated repository queries that are annotated with
-     * <code>@Query</code> and return a {@link Page} or {@link KeysetAwarePage}.
-     * Slices do not use a counting query.</p>
+     * the {@link Page#totalElements total elements} and {@link Page#totalPages
+     * total pages} for paginated repository queries which are annotated with
+     * {@code @Query} and return a {@link Page} or {@link CursoredPage}.</p>
      *
      * <p>The default value of empty string indicates that no counting query
      * is provided. A counting query is unnecessary when pagination is
diff --git a/api/src/main/java/module-info.java b/api/src/main/java/module-info.java
index 8af2fab6..f9bac991 100644
--- a/api/src/main/java/module-info.java
+++ b/api/src/main/java/module-info.java
@@ -530,8 +530,7 @@ import java.util.List;
  * all streams that it obtains from repository methods.</td></tr>
  *
  * <tr style="vertical-align: top"><td><code>find...By...(..., PageRequest)</code></td>
- * <td><code>Page&lt;E&gt;</code>, <code>KeysetAwarePage&lt;E&gt;</code>,
- * <br><code>Slice&lt;E&gt;</code>, <code>KeysetAwareSlice&lt;E&gt;</code></td>
+ * <td><code>Page&lt;E&gt;</code>, <code>CursoredPage&lt;E&gt;</code></td>
  * <td>For use with pagination</td></tr>
  *
  * </table>
diff --git a/spec/src/main/asciidoc/chapters/introduction/introduction.asciidoc b/spec/src/main/asciidoc/chapters/introduction/introduction.asciidoc
index e2d8f772..825322c3 100644
--- a/spec/src/main/asciidoc/chapters/introduction/introduction.asciidoc
+++ b/spec/src/main/asciidoc/chapters/introduction/introduction.asciidoc
@@ -60,6 +60,4 @@ The following are not goals of Jakarta Data:
 
 The terms, _entity attribute_ and _entity property_, are used interchangeably throughout the specification.
 
-The lower case terms, _slice_ and _page_, are used interchangeably throughout the specification. Where there is a need to distinguish, the class name, `Slice` or `Page`, is used.
-
 include::project_team.adoc[]
diff --git a/spec/src/main/asciidoc/repository.asciidoc b/spec/src/main/asciidoc/repository.asciidoc
index 0f2d6a96..1b0d61c3 100644
--- a/spec/src/main/asciidoc/repository.asciidoc
+++ b/spec/src/main/asciidoc/repository.asciidoc
@@ -1194,7 +1194,7 @@ List<User> findByNameStartsWith(String namePrefix, Sort<?>... sorts);
 // Sorts first by name. When name is the same, applies the PageRequest's sort criteria
 @Query("SELECT u FROM User u WHERE (u.age > ?1)")
 @OrderBy("name")
-KeysetAwarePage<User> olderThan(int age, PageRequest<User> pagination);
+CursoredPage<User> olderThan(int age, PageRequest<User> pagination);
 ----
 
 === Pagination in Jakarta Data
@@ -1233,11 +1233,12 @@ Offset pagination offers several key features that make it a valuable approach f
 
 The following requirements must be met when using offset-based pagination:
 
-* The repository method signature must return `Slice` or `Page`. A repository method with return type of `Slice` or `Page` must raise `UnsupportedOperationException` if the database is incapable of offset pagination.
+* The repository method signature must return `Page`. A repository method with return type of `Page` must raise `UnsupportedOperationException` if the database is incapable of offset 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 define a deterministic ordering of entities.
 * The entities within each page must be ordered according to the provided sort criteria.
+* If `PageRequest.requestTotal()` returns `true`, the `Page` should contain accurate information about the total number of pages and total number of elements across all pages. Otherwise, if `PageRequest.requestTotal()` returns `false`, the operations `Page.totalElements()` and `Page.totalPages()` throw `IllegalStateException`.
 * Except for the highest numbered page, the Jakarta Data provider must return full pages consisting of the maximum page size number of entities.
 * Page numbers for offset pagination are computed by taking the entity's 1-based offset after sorting, dividing it by the maximum page size, and rounding up. For example, the 52nd entity is on page 6 when the maximum page size is 10, because 52 / 10 rounded up is 6. Note that the first page number is always 1.
 
@@ -1332,7 +1333,7 @@ Offset pagination is a valuable tool for Java developers when dealing with large
 
 ==== Keyset 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 `KeysetAwareSlice` or `KeysetAwarePage` are 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.
+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.
 
 For example,
 
@@ -1340,7 +1341,7 @@ For example,
 ----
 @Repository
 public interface CustomerRepository extends BasicRepository<Customer, Long> {
-  KeysetAwareSlice<Customer> findByZipcodeOrderByLastNameAscFirstNameAscIdAsc(
+  CursoredPage<Customer> findByZipcodeOrderByLastNameAscFirstNameAscIdAsc(
                                  int zipcode, PageRequest<Customer> pageRequest);
 }
 ----
@@ -1405,7 +1406,7 @@ Other types of updates to data, however, will cause duplicate or missed results.
 
 ===== Restrictions on use of Keyset Pagination
 
-* The repository method signature must return `KeysetAwareSlice` or `KeysetAwarePage`. A repository method with return type of `KeysetAwareSlice` or `KeysetAwarePage` 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 keyset 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.
@@ -1424,7 +1425,7 @@ Here is an example where an application uses `@Query` to provide a partial query
 @Repository
 public interface CustomerRepository extends BasicRepository<Customer, Long> {
   @Query("SELECT o FROM Customer o WHERE (o.totalSpent / o.totalPurchases > ?1)")
-  KeysetAwareSlice<Customer> withAveragePurchaseAbove(float minimum, PageRequest<Customer> pageRequest);
+  CursoredPage<Customer> withAveragePurchaseAbove(float minimum, PageRequest<Customer> pageRequest);
 }
 ----
 
@@ -1452,7 +1453,7 @@ In this example, the application uses a cursor to request pages in forward and p
 ----
 @Repository
 public interface Products extends CrudRepository<Product, Long> {
-  KeysetAwareSlice<Product> findByNameContains(String namePattern, PageRequest<Product> pageRequest);
+  CursoredPage<Product> findByNameContains(String namePattern, PageRequest<Product> pageRequest);
 }
 ----
 
@@ -1464,7 +1465,7 @@ float priceMidpoint = 50.0f;
 PageRequest<Product> pageRequest = Order.by(_Product.price.asc(), _Product.id.asc())
                                         .pageSize(10)
                                         .afterKeyset(priceMidpoint, 0L);
-KeysetAwareSlice<Product> moreExpensive = products.findByNameContains(pattern, pageRequest);
+CursoredPage<Product> moreExpensive = products.findByNameContains(pattern, pageRequest);
 ----
 
 Obtaining the previous 10 products:
@@ -1475,7 +1476,7 @@ if (moreExpensive.hasContent())
     pageRequest = pageRequest.beforeKeysetCursor(moreExpensive.getKeysetCursor(0));
 else
     pageRequest = pageRequest.beforeKeyset(priceMidpoint, 1L);
-KeysetAwareSlice<Product> lessExpensive = products.findByNameContains(pattern, pageRequest);
+CursoredPage<Product> lessExpensive = products.findByNameContains(pattern, pageRequest);
 ----
 
 ===== Example with Combined Sort Criteria
@@ -1487,7 +1488,7 @@ In this example, the application uses `OrderBy` to define a subset of the sort c
 @Repository
 public interface Products extends CrudRepository<Product, Long> {
   @OrderBy(_Car.vehicleCondition)
-  KeysetAwarePage<Car> find(@By(_Car.make) String manufacturer,
+  CursoredPage<Car> find(@By(_Car.make) String manufacturer,
                             @By(_Car.model) String model,
                             PageRequest<Car> pageRequest);
 }
@@ -1499,7 +1500,7 @@ The above criteria does not uniquely identify `Car` entities. After sorting on t
 ----
 PageRequest<Car> page1Request = Order.by(_Car.price.desc(), _Car.vin.asc())
                                      .pageSize(25);
-KeysetAwarePage<Car> page1 = cars.find(make, model, page1Request);
+CursoredPage<Car> page1 = cars.find(make, model, page1Request);
 ----
 
 The query results are ordered first by vehicle condition. All resulting entities with the same vehicle condition are subsequently ordered by their price in descending order. All resulting entities with the same vehicle condition and price are ordered alphabetically by their VIN. The end user requests the next page of results. If the application still has access to the page at this point, it can use `page.nextPageRequest()` to obtain a request for the next page of results. In this case, the Jakarta Data provider computes the cursor from the vehicle condition, price, and VIN of the final `Car` entity of the page and includes the cursor in the resulting `PageRequest` instance. Alternatively, the application does not need access to the page if it obtained the cursor or the vehicle condition, price, and VIN values that make up the cursor. In this case, it can construct a new `PageRequest`,
@@ -1512,7 +1513,7 @@ PageRequest<Car> page2Request = Order.by(_Car.price.desc(), _Car.vin.asc())
                                      .afterKeyset(lastCar.vehicleCondition,
                                                   lastCar.price,
                                                   lastCar.vin);
-KeysetAwarePage<Car> page2 = cars.find(make, model, page2Request);
+CursoredPage<Car> page2 = cars.find(make, model, page2Request);
 ----
 
 ===== Scenario: Person Entity and People Repository
@@ -1540,7 +1541,7 @@ This keyset cursor-based pagination scenario uses the same `Person` entity and e
 @Repository
 public interface People extends BasicRepository<Person, Long> {
     @Find
-    KeysetAwarePage<Person> findAll(PageRequest<Person> pagination);
+    CursoredPage<Person> findAll(PageRequest<Person> pagination);
 }
 ----
 
@@ -1555,7 +1556,7 @@ PageRequest<Person> firstPageRequest = PageRequest.of(Person.class)
                                                   .size(4)
                                                   .sortBy(Sort.asc("name"),
                                                           Sort.asc("id"));
-KeysetAwarePage<Person> page = people.findAll(firstPageRequest);
+CursoredPage<Person> page = people.findAll(firstPageRequest);
 ----
 
 Resulting Page Content:
@@ -1585,7 +1586,7 @@ Next Page Execution:
 ----
 if (page.hasNext()) {
    PageRequest<Person> nextPageRequest = page.nextPageRequest();
-   KeysetAwarePage<Person> page2 = people.findAll(nextPageRequest);
+   CursoredPage<Person> page2 = people.findAll(nextPageRequest);
 }
 ----
 
diff --git a/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/AsciiCharacters.java b/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/AsciiCharacters.java
index 73c0fa0a..52bdb8eb 100644
--- a/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/AsciiCharacters.java
+++ b/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/AsciiCharacters.java
@@ -22,7 +22,6 @@ import java.util.stream.Stream;
 
 import jakarta.data.Order;
 import jakarta.data.Sort;
-import jakarta.data.Streamable;
 import jakarta.data.page.Page;
 import jakarta.data.page.PageRequest;
 import jakarta.data.repository.By;
@@ -65,7 +64,7 @@ public interface AsciiCharacters extends DataRepository<AsciiCharacter, Long>, I
 
     Page<AsciiCharacter> findByNumericValueBetween(int min, int max, PageRequest<AsciiCharacter> pagination);
 
-    Streamable<AsciiCharacter> findByNumericValueLessThanEqualAndNumericValueGreaterThanEqual(int max, int min);
+//    Streamable<AsciiCharacter> findByNumericValueLessThanEqualAndNumericValueGreaterThanEqual(int max, int min);
 
     AsciiCharacter[] findFirst3ByNumericValueGreaterThanEqualAndHexadecimalEndsWith(long minValue, String lastHexDigit, Sort<AsciiCharacter> sort);
 
diff --git a/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/NaturalNumbers.java b/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/NaturalNumbers.java
index 43301619..94c4d3f8 100644
--- a/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/NaturalNumbers.java
+++ b/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/NaturalNumbers.java
@@ -19,9 +19,9 @@ import java.util.stream.Stream;
 
 import jakarta.data.Limit;
 import jakarta.data.Order;
-import jakarta.data.page.KeysetAwareSlice;
+import jakarta.data.page.CursoredPage;
+import jakarta.data.page.Page;
 import jakarta.data.page.PageRequest;
-import jakarta.data.page.Slice;
 import jakarta.data.repository.BasicRepository;
 import jakarta.data.repository.Repository;
 
@@ -38,24 +38,24 @@ public interface NaturalNumbers extends BasicRepository<NaturalNumber, Long>, Id
 
     long countBy();
 
-    KeysetAwareSlice<NaturalNumber> findByFloorOfSquareRootOrderByIdAsc(long sqrtFloor,
-                                                                        PageRequest<NaturalNumber> pagination);
+    CursoredPage<NaturalNumber> findByFloorOfSquareRootOrderByIdAsc(long sqrtFloor,
+                                                                    PageRequest<NaturalNumber> pagination);
 
     Stream<NaturalNumber> findByIdBetweenOrderByNumTypeAsc(long minimum,
                                                            long maximum,
                                                            Order<NaturalNumber> sorts);
 
-    Slice<NaturalNumber> findByIdLessThanOrderByFloorOfSquareRootDesc(long exclusiveMax,
-                                                                      PageRequest<NaturalNumber> pagination);
+    Page<NaturalNumber> findByIdLessThanOrderByFloorOfSquareRootDesc(long exclusiveMax,
+                                                                     PageRequest<NaturalNumber> pagination);
 
-    KeysetAwareSlice<NaturalNumber> findByNumTypeAndNumBitsRequiredLessThan(NumberType type,
-                                                                            short bitsUnder,
-                                                                            PageRequest<NaturalNumber> pagination);
+    CursoredPage<NaturalNumber> findByNumTypeAndNumBitsRequiredLessThan(NumberType type,
+                                                                        short bitsUnder,
+                                                                        PageRequest<NaturalNumber> pagination);
 
     NaturalNumber[] findByNumTypeNot(NumberType notThisType, Limit limit, Order<NaturalNumber> sorts);
 
-    Slice<NaturalNumber> findByNumTypeAndFloorOfSquareRootLessThanEqual(NumberType type,
-                                                                        long maxSqrtFloor,
-                                                                        PageRequest<NaturalNumber> pagination);
+    Page<NaturalNumber> findByNumTypeAndFloorOfSquareRootLessThanEqual(NumberType type,
+                                                                       long maxSqrtFloor,
+                                                                       PageRequest<NaturalNumber> pagination);
 
 }
diff --git a/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/PositiveIntegers.java b/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/PositiveIntegers.java
index 00337705..9442a582 100644
--- a/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/PositiveIntegers.java
+++ b/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/PositiveIntegers.java
@@ -23,8 +23,7 @@ import java.util.stream.Stream;
 import ee.jakarta.tck.data.framework.read.only.NaturalNumber.NumberType;
 import jakarta.data.Limit;
 import jakarta.data.Order;
-import jakarta.data.Streamable;
-import jakarta.data.page.KeysetAwarePage;
+import jakarta.data.page.CursoredPage;
 import jakarta.data.page.Page;
 import jakarta.data.page.PageRequest;
 import jakarta.data.repository.Find;
@@ -42,13 +41,13 @@ public interface PositiveIntegers extends BasicRepository<NaturalNumber, Long> {
 
     boolean existsByIdGreaterThan(Long number);
 
-    KeysetAwarePage<NaturalNumber> findByFloorOfSquareRootNotAndIdLessThanOrderByBitsRequiredDesc(long excludeSqrt,
+    CursoredPage<NaturalNumber> findByFloorOfSquareRootNotAndIdLessThanOrderByBitsRequiredDesc(long excludeSqrt,
                                                                                                   long eclusiveMax,
                                                                                                   PageRequest<NaturalNumber> pagination);
 
     Iterable<NaturalNumber> findByIsOddTrueAndIdLessThanEqualOrderByIdDesc(long max);
 
-    Streamable<NaturalNumber> findByIsOddFalseAndIdBetween(long min, long max);
+    List<NaturalNumber> findByIsOddFalseAndIdBetween(long min, long max);
 
     Stream<NaturalNumber> findByNumTypeInOrderByIdAsc(Set<NumberType> types, Limit limit);
 
diff --git a/tck/src/main/java/ee/jakarta/tck/data/standalone/persistence/Catalog.java b/tck/src/main/java/ee/jakarta/tck/data/standalone/persistence/Catalog.java
index 71c3cb38..acd0bb9e 100644
--- a/tck/src/main/java/ee/jakarta/tck/data/standalone/persistence/Catalog.java
+++ b/tck/src/main/java/ee/jakarta/tck/data/standalone/persistence/Catalog.java
@@ -22,7 +22,6 @@ import java.util.Optional;
 import java.util.stream.Stream;
 
 import jakarta.data.Order;
-import jakarta.data.Streamable;
 import jakarta.data.repository.DataRepository;
 import jakarta.data.repository.Delete;
 import jakarta.data.repository.Find;
@@ -73,7 +72,7 @@ public interface Catalog extends DataRepository<Product, String> {
     int countBySurgePriceGreaterThanEqual(Double price);
 
     @Query("SELECT p FROM Product p WHERE (SIZE(p.departments) = ?1 AND p.price < ?2) ORDER BY p.name")
-    Streamable<Product> findByDepartmentCountAndPriceBelow(int numDepartments, double maxPrice);
+    List<Product> findByDepartmentCountAndPriceBelow(int numDepartments, double maxPrice);
 
     @OrderBy("name")
     Product[] findByDepartmentsContains(Department department);


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 0a6f9f4b..6fca46ed 100644
--- a/api/src/test/java/jakarta/data/page/KeysetPageRequestTest.java
+++ b/api/src/test/java/jakarta/data/page/KeysetPageRequestTest.java
@@ -50,7 +50,7 @@ class KeysetPageRequestTest {
     @Test
     @DisplayName("Should include keyset values in next PageRequest from Cursor")
     void shouldCreatePageRequestAfterKeysetCursor() {
-        PageRequest.Cursor cursor = new KeysetCursor("me", 200);
+        PageRequest.Cursor cursor = new PageRequestCursor("me", 200);
         PageRequest<?> pageRequest = PageRequest.ofSize(35).sortBy(Sort.asc("name"), Sort.asc("id")).afterKeysetCursor(cursor);
 
         assertSoftly(softly -> {
@@ -83,7 +83,7 @@ class KeysetPageRequestTest {
     @Test
     @DisplayName("Should include keyset values in previous PageRequest from Cursor")
     void shouldCreatePageRequestBeforeKeysetCursor() {
-        PageRequest.Cursor cursor = new KeysetCursor(900L, 300, "testing", 120, 'T');
+        PageRequest.Cursor cursor = new PageRequestCursor(900L, 300, "testing", 120, 'T');
         PageRequest<?> pageRequest = PageRequest.ofPage(8).beforeKeysetCursor(cursor);
 
         assertSoftly(softly -> {
@@ -144,15 +144,15 @@ class KeysetPageRequestTest {
     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 KeysetCursor("keyval1", '2', 3));
-        PageRequest<?> pageRequest25P2S0A1 = PageRequest.ofPage(2).size(25).afterKeysetCursor(new KeysetCursor("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.Cursor cursor1 = new KeysetCursor("keyval1", '2', 3);
-        PageRequest.Cursor cursor2 = new KeysetCursor("keyval2", '2', 3);
-        PageRequest.Cursor cursor3 = new KeysetCursor("keyval1", '2');
+        PageRequest.Cursor cursor1 = new PageRequestCursor("keyval1", '2', 3);
+        PageRequest.Cursor cursor2 = new PageRequestCursor("keyval2", '2', 3);
+        PageRequest.Cursor cursor3 = new PageRequestCursor("keyval1", '2');
         PageRequest.Cursor cursor4 = new PageRequest.Cursor() {
             private final Object[] keyset = new Object[] { "keyval1", '2', 3 };
 
diff --git a/api/src/test/java/jakarta/data/page/PaginationTest.java b/api/src/test/java/jakarta/data/page/PaginationTest.java
index c026f354..ffc4daf0 100644
--- a/api/src/test/java/jakarta/data/page/PaginationTest.java
+++ b/api/src/test/java/jakarta/data/page/PaginationTest.java
@@ -39,9 +39,9 @@ class PaginationTest {
     @DisplayName("Should throw UnsupportedOperationException when keyset is not supported")
     void shouldThrowExceptionWhenKeysetIsNotSupported() {
         assertThatThrownBy(() -> {
-            Pagination<?> pagination = new Pagination<>(1, 10, Collections.emptyList(), PageRequest.Mode.CURSOR_NEXT, new KeysetCursor("me", 200));
+            Pagination<?> pagination = new Pagination<>(1, 10, Collections.emptyList(), PageRequest.Mode.CURSOR_NEXT, new PageRequestCursor("me", 200));
             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 KeysetAwareSlice.");
+                .hasMessage("Not supported for keyset pagination. Instead use afterKeyset or afterKeysetCursor to provide the next keyset values 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 e4ba2032..04fbb958 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
@@ -35,6 +35,7 @@ import java.util.logging.Logger;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import jakarta.data.page.CursoredPage;
 import org.jboss.arquillian.container.test.api.Deployment;
 import org.jboss.shrinkwrap.api.ShrinkWrap;
 import org.jboss.shrinkwrap.api.spec.JavaArchive;
@@ -59,15 +60,11 @@ import ee.jakarta.tck.data.framework.utilities.TestPropertyUtility;
 import jakarta.data.Limit;
 import jakarta.data.Order;
 import jakarta.data.Sort;
-import jakarta.data.Streamable;
 import jakarta.data.exceptions.EmptyResultException;
 import jakarta.data.exceptions.MappingException;
 import jakarta.data.exceptions.NonUniqueResultException;
-import jakarta.data.page.KeysetAwarePage;
-import jakarta.data.page.KeysetAwareSlice;
 import jakarta.data.page.Page;
 import jakarta.data.page.PageRequest;
-import jakarta.data.page.Slice;
 import jakarta.inject.Inject;
 
 /**
@@ -425,13 +422,13 @@ public class EntityTests {
 
     @Assertion(id = "133", strategy = "Request a Slice higher than the final Slice, expecting an empty Slice with 0 results.")
     public void testBeyondFinalSlice() {
-        PageRequest<NaturalNumber> sixth = PageRequest.of(NaturalNumber.class).size(5).sortBy(Sort.desc("id")).page(6);
-        Slice<NaturalNumber> slice = numbers.findByNumTypeAndFloorOfSquareRootLessThanEqual(NumberType.PRIME, 8L,
+        PageRequest<NaturalNumber> sixth = PageRequest.of(NaturalNumber.class).size(5).sortBy(Sort.desc("id")).page(6).withoutTotal();
+        Page<NaturalNumber> page = numbers.findByNumTypeAndFloorOfSquareRootLessThanEqual(NumberType.PRIME, 8L,
                 sixth);
-        assertEquals(0, slice.numberOfElements());
-        assertEquals(0, slice.stream().count());
-        assertEquals(false, slice.hasContent());
-        assertEquals(false, slice.iterator().hasNext());
+        assertEquals(0, page.numberOfElements());
+        assertEquals(0, page.stream().count());
+        assertEquals(false, page.hasContent());
+        assertEquals(false, page.iterator().hasNext());
     }
 
     @Assertion(id = "133", strategy = "Use a parameter-based find operation that uses the By annotation to identify the entity attribute names.")
@@ -537,7 +534,7 @@ public class EntityTests {
 
     @Assertion(id = "133", strategy = "Use a repository method with the False keyword.")
     public void testFalse() {
-        Streamable<NaturalNumber> even = positives.findByIsOddFalseAndIdBetween(50L, 60L);
+        List<NaturalNumber> even = positives.findByIsOddFalseAndIdBetween(50L, 60L);
 
         assertEquals(6L, even.stream().count());
 
@@ -595,14 +592,14 @@ public class EntityTests {
 
     @Assertion(id = "133", strategy = "Request the last Slice of up to 5 results, expecting to find the final 2.")
     public void testFinalSliceOfUpTo5() {
-        PageRequest<NaturalNumber> fifth = PageRequest.of(NaturalNumber.class).size(5).page(5).sortBy(Sort.desc("id"));
-        Slice<NaturalNumber> slice = numbers.findByNumTypeAndFloorOfSquareRootLessThanEqual(NumberType.PRIME, 8L,
+        PageRequest<NaturalNumber> fifth = PageRequest.of(NaturalNumber.class).size(5).page(5).sortBy(Sort.desc("id")).withoutTotal();
+        Page<NaturalNumber> page = numbers.findByNumTypeAndFloorOfSquareRootLessThanEqual(NumberType.PRIME, 8L,
                 fifth);
-        assertEquals(true, slice.hasContent());
-        assertEquals(5, slice.pageRequest().page());
-        assertEquals(2, slice.numberOfElements());
+        assertEquals(true, page.hasContent());
+        assertEquals(5, page.pageRequest().page());
+        assertEquals(2, page.numberOfElements());
 
-        Iterator<NaturalNumber> it = slice.iterator();
+        Iterator<NaturalNumber> it = page.iterator();
 
         // first result
         assertEquals(true, it.hasNext());
@@ -757,10 +754,10 @@ public class EntityTests {
     }
 
     @Assertion(id = "133",
-               strategy = "Request the first KeysetAwarePage of 8 results, expecting to find all 8, " +
-                          "then request the next KeysetAwarePage and the KeysetAwarePage after that, " +
+               strategy = "Request the first CursoredPage of 8 results, expecting to find all 8, " +
+                          "then request the next CursoredPage and the CursoredPage after that, " +
                           "expecting to find all results.")
-    public void testFirstKeysetAwarePageOf8AndNextPages() {
+    public void testFirstCursoredPageOf8AndNextPages() {
         // The query for this test returns 1-15,25-32 in the following order:
 
         // 25, 26, 27, 28, 29, 30, 31, 32 square root rounds down to 4
@@ -769,7 +766,7 @@ public class EntityTests {
         // 1, 2, 3 square root rounds down to 1
 
         PageRequest<NaturalNumber> first8 = PageRequest.of(NaturalNumber.class).size(8).sortBy(Sort.asc("id"));
-        KeysetAwarePage<NaturalNumber> page;
+        CursoredPage<NaturalNumber> page;
 
         try {
             page = positives.findByFloorOfSquareRootNotAndIdLessThanOrderByBitsRequiredDesc(4L, 33L, first8);
@@ -812,12 +809,12 @@ public class EntityTests {
     }
 
     @Assertion(id = "133",
-               strategy = "Request the first KeysetAwareSlice of 6 results, expecting to find all 6, " +
-                          "then request the next KeysetAwareSlice and the KeysetAwareSlice after that, " +
+               strategy = "Request the first CursoredPage of 6 results, expecting to find all 6, " +
+                          "then request the next CursoredPage and the CursoredPage after that, " +
                           "expecting to find all results.")
     public void testFirstKeysetAwareSliceOf6AndNextSlices() {
-        PageRequest<NaturalNumber> first6 = PageRequest.ofSize(6);
-        KeysetAwareSlice<NaturalNumber> slice;
+        PageRequest<NaturalNumber> first6 = PageRequest.of(NaturalNumber.class).size(6).withoutTotal();
+        CursoredPage<NaturalNumber> slice;
 
         try {
             slice = numbers.findByFloorOfSquareRootOrderByIdAsc(7L, first6);
@@ -887,12 +884,12 @@ public class EntityTests {
 
     @Assertion(id = "133", strategy = "Request the first Slice of 5 results, expecting to find all 5.")
     public void testFirstSliceOf5() {
-        PageRequest<NaturalNumber> first5 = PageRequest.of(NaturalNumber.class).size(5).sortBy(Sort.desc("id"));
-        Slice<NaturalNumber> slice = numbers.findByNumTypeAndFloorOfSquareRootLessThanEqual(NumberType.PRIME, 8L,
+        PageRequest<NaturalNumber> first5 = PageRequest.of(NaturalNumber.class).size(5).sortBy(Sort.desc("id")).withoutTotal();
+        Page<NaturalNumber> page = numbers.findByNumTypeAndFloorOfSquareRootLessThanEqual(NumberType.PRIME, 8L,
                 first5);
-        assertEquals(5, slice.numberOfElements());
+        assertEquals(5, page.numberOfElements());
 
-        Iterator<NaturalNumber> it = slice.iterator();
+        Iterator<NaturalNumber> it = page.iterator();
 
         // first result
         assertEquals(true, it.hasNext());
@@ -972,10 +969,10 @@ public class EntityTests {
     }
 
     @Assertion(id = "133",
-               strategy = "Request a KeysetAwarePage of 7 results after the keyset of the 20th result, expecting to find the next 7 results. " +
-                          "Then request the KeysetAwarePage before the keyset of the first entry of the page, expecting to find the previous 7 results. " +
-                          "Then request the KeysetAwarePage after the last entry of the original slice, expecting to find the next 7.")
-    public void testKeysetAwarePageOf7FromCursor() {
+               strategy = "Request a CursoredPage of 7 results after the keyset of the 20th result, expecting to find the next 7 results. " +
+                          "Then request the CursoredPage before the keyset of the first entry of the page, expecting to find the previous 7 results. " +
+                          "Then request the CursoredPage after the last entry of the original slice, expecting to find the next 7.")
+    public void testCursoredPageOf7FromCursor() {
         // The query for this test returns 1-35 and 49 in the following order:
         //
         // 35 34 33 32 49 24 23 22 21 20 19 18 17 16 31 30 29 28 27 26 25 08 15 14 13 12 11 10 09 07 06 05 04 03 02 01
@@ -987,7 +984,7 @@ public class EntityTests {
                         .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.
 
-        KeysetAwarePage<NaturalNumber> page;
+        CursoredPage<NaturalNumber> page;
         try {
             page = positives.findByFloorOfSquareRootNotAndIdLessThanOrderByBitsRequiredDesc(6L, 50L, middle7);
         } catch (MappingException x) {
@@ -1006,7 +1003,7 @@ public class EntityTests {
 
         assertEquals(true, page.hasPrevious());
 
-        KeysetAwarePage<NaturalNumber> previousPage;
+        CursoredPage<NaturalNumber> previousPage;
         try {
             previousPage = positives.findByFloorOfSquareRootNotAndIdLessThanOrderByBitsRequiredDesc(6L, 50L,
                                                                                                     page.previousPageRequest());
@@ -1023,7 +1020,7 @@ public class EntityTests {
 
         assertEquals(7, previousPage.numberOfElements());
 
-        KeysetAwarePage<NaturalNumber> nextPage;
+        CursoredPage<NaturalNumber> nextPage;
         try {
             nextPage = positives.findByFloorOfSquareRootNotAndIdLessThanOrderByBitsRequiredDesc(6L, 50L,
                                                                                                 page.nextPageRequest());
@@ -1042,10 +1039,10 @@ public class EntityTests {
         assertEquals(7, nextPage.numberOfElements());
     }
 
-    @Assertion(id = "133", strategy = "Request a KeysetAwarePage of results where none match the query, expecting an empty KeysetAwarePage with 0 results.")
-    public void testKeysetAwarePageOfNothing() {
+    @Assertion(id = "133", strategy = "Request a CursoredPage of results where none match the query, expecting an empty CursoredPage with 0 results.")
+    public void testCursoredPageOfNothing() {
 
-        KeysetAwarePage<NaturalNumber> page;
+        CursoredPage<NaturalNumber> page;
         try {
             // There are no positive integers less than 4 which have a square root that rounds down to something other than 1.
             page = positives.findByFloorOfSquareRootNotAndIdLessThanOrderByBitsRequiredDesc(1L, 4L, PageRequest.ofPage(1L));
@@ -1084,9 +1081,9 @@ public class EntityTests {
     }
 
     @Assertion(id = "133",
-            strategy = "Request a KeysetAwareSlice of 9 results after the keyset of the 20th result, expecting to find the next 9 results. " +
-                       "Then request the KeysetAwareSlice before the keyset of the first entry of the slice, expecting to find the previous 9 results. " +
-                       "Then request the KeysetAwareSlice after the last entry of the original slice, expecting to find the next 9.")
+            strategy = "Request a CursoredPage of 9 results after the keyset of the 20th result, expecting to find the next 9 results. " +
+                       "Then request the CursoredPage before the keyset of the first entry of the slice, expecting to find the previous 9 results. " +
+                       "Then request the CursoredPage after the last entry of the original slice, expecting to find the next 9.")
     public void testKeysetAwareSliceOf9FromCursor() {
         // The query for this test returns composite natural numbers under 64 in the following order:
         //
@@ -1095,11 +1092,11 @@ public class EntityTests {
         //                                  ^^^^^^^^ slice 2 ^^^^^^^^^
         //                                                                                        ^^^^^^^^ slice 3 ^^^^^^^^^
 
-        PageRequest<NaturalNumber> middle9 = PageRequest.of(NaturalNumber.class).size(9)
+        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.
 
-        KeysetAwareSlice<NaturalNumber> slice;
+        CursoredPage<NaturalNumber> slice;
         try {
             slice = numbers.findByNumTypeAndNumBitsRequiredLessThan(NumberType.COMPOSITE, (short) 7, middle9);
         } catch (MappingException x) {
@@ -1114,8 +1111,7 @@ public class EntityTests {
         assertEquals(9, slice.numberOfElements());
 
         assertEquals(true, slice.hasPrevious());
-
-        KeysetAwareSlice<NaturalNumber> previousSlice;
+        CursoredPage<NaturalNumber> previousSlice;
         try {
             previousSlice = numbers.findByNumTypeAndNumBitsRequiredLessThan(NumberType.COMPOSITE,
                                                                             (short) 7,
@@ -1131,7 +1127,7 @@ public class EntityTests {
 
          assertEquals(9, previousSlice.numberOfElements());
 
-         KeysetAwareSlice<NaturalNumber> nextSlice;
+         CursoredPage<NaturalNumber> nextSlice;
          try {
              nextSlice = numbers.findByNumTypeAndNumBitsRequiredLessThan(NumberType.COMPOSITE,
                                                                          (short) 7,
@@ -1148,12 +1144,12 @@ public class EntityTests {
          assertEquals(9, nextSlice.numberOfElements());
     }
 
-    @Assertion(id = "133", strategy = "Request a KeysetAwareSlice of results where none match the query, expecting an empty KeysetAwareSlice with 0 results.")
+    @Assertion(id = "133", strategy = "Request a CursoredPage of results where none match the query, expecting an empty CursoredPage with 0 results.")
     public void testKeysetAwareSliceOfNothing() {
         // 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);
+        PageRequest<NaturalNumber> pagination = PageRequest.of(NaturalNumber.class).size(33).afterKeyset(30L).withoutTotal();
 
-        KeysetAwareSlice<NaturalNumber> slice;
+        CursoredPage<NaturalNumber> slice;
         try {
             slice = numbers.findByFloorOfSquareRootOrderByIdAsc(3L, pagination);
         } catch (MappingException x) {
@@ -1281,35 +1277,35 @@ public class EntityTests {
                           "followed by the dynamic sort criteria when the value(s) being compared by the static criteria match.")
     public void testOrderByHasPrecedenceOverPageRequestSorts() {
         PageRequest<NaturalNumber> pagination = PageRequest.of(NaturalNumber.class).size(8).sortBy(Sort.asc("numType"), Sort.desc("id"));
-        Slice<NaturalNumber> slice = numbers.findByIdLessThanOrderByFloorOfSquareRootDesc(25L, pagination);
+        Page<NaturalNumber> page = numbers.findByIdLessThanOrderByFloorOfSquareRootDesc(25L, pagination);
 
         assertEquals(Arrays.toString(new Long[] { 23L, 19L, 17L, // square root rounds down to 4; prime
                                                   24L, 22L, 21L, 20L, 18L }), // square root rounds down to 4; composite
-                     Arrays.toString(slice.stream().map(number -> number.getId()).toArray()));
+                     Arrays.toString(page.stream().map(number -> number.getId()).toArray()));
 
-        assertEquals(true, slice.hasNext());
-        pagination = slice.nextPageRequest();
-        slice = numbers.findByIdLessThanOrderByFloorOfSquareRootDesc(25L, pagination);
+        assertEquals(true, page.hasNext());
+        pagination = page.nextPageRequest();
+        page = numbers.findByIdLessThanOrderByFloorOfSquareRootDesc(25L, pagination);
 
         assertEquals(Arrays.toString(new Long[] { 16L, // square root rounds down to 4; composite
                                                   13L, 11L, // square root rounds down to 3; prime
                                                   15L, 14L, 12L, 10L, 9L }), // square root rounds down to 3; composite
-                     Arrays.toString(slice.stream().map(number -> number.getId()).toArray()));
+                     Arrays.toString(page.stream().map(number -> number.getId()).toArray()));
 
-        assertEquals(true, slice.hasNext());
-        pagination = slice.nextPageRequest();
-        slice = numbers.findByIdLessThanOrderByFloorOfSquareRootDesc(25L, pagination);
+        assertEquals(true, page.hasNext());
+        pagination = page.nextPageRequest();
+        page = numbers.findByIdLessThanOrderByFloorOfSquareRootDesc(25L, pagination);
 
         assertEquals(Arrays.toString(new Long[] { 7L, 5L, // square root rounds down to 2; prime
                                                   8L, 6L, 4L, // square root rounds down to 2; composite
                                                   1L, // square root rounds down to 1; one
                                                   3L, 2L }), // square root rounds down to 1; prime
-                     Arrays.toString(slice.stream().map(number -> number.getId()).toArray()));
+                     Arrays.toString(page.stream().map(number -> number.getId()).toArray()));
 
-        if (slice.hasNext()) {
-            pagination = slice.nextPageRequest();
-            slice = numbers.findByIdLessThanOrderByFloorOfSquareRootDesc(25L, pagination);
-            assertEquals(false, slice.hasContent());
+        if (page.hasNext()) {
+            pagination = page.nextPageRequest();
+            page = numbers.findByIdLessThanOrderByFloorOfSquareRootDesc(25L, pagination);
+            assertEquals(false, page.hasContent());
         }
     }
 
@@ -1372,13 +1368,13 @@ public class EntityTests {
 
     @Assertion(id = "133", strategy = "Request a Slice of results where none match the query, expecting an empty Slice with 0 results.")
     public void testSliceOfNothing() {
-        PageRequest<NaturalNumber> pagination =  PageRequest.of(NaturalNumber.class).size(5).sortBy(Sort.desc("id"));
-        Slice<NaturalNumber> slice = numbers.findByNumTypeAndFloorOfSquareRootLessThanEqual(NumberType.COMPOSITE, 1L,
+        PageRequest<NaturalNumber> pagination =  PageRequest.of(NaturalNumber.class).size(5).sortBy(Sort.desc("id")).withoutTotal();
+        Page<NaturalNumber> page = numbers.findByNumTypeAndFloorOfSquareRootLessThanEqual(NumberType.COMPOSITE, 1L,
                 pagination);
 
-        assertEquals(false, slice.hasContent());
-        assertEquals(0, slice.content().size());
-        assertEquals(0, slice.numberOfElements());
+        assertEquals(false, page.hasContent());
+        assertEquals(0, page.content().size());
+        assertEquals(0, page.numberOfElements());
     }
 
     @Assertion(id = "133", strategy = "Use the StaticMetamodel to obtain ascending Sorts for an entity attribute in a type-safe manner.")
@@ -1458,34 +1454,34 @@ public class EntityTests {
         assertEquals('d', found[1].getThisCharacter());
         assertEquals('T', found[2].getThisCharacter());
     }
-
-    @Assertion(id = "133", strategy = "Use a repository method that returns Streamable and verify the results.")
-    public void testStreamable() {
-        Streamable<AsciiCharacter> chars = characters.findByNumericValueLessThanEqualAndNumericValueGreaterThanEqual(109, 101);
-
-        assertEquals(Arrays.toString(new Character[] { Character.valueOf('e'),
-                                                       Character.valueOf('f'),
-                                                       Character.valueOf('g'),
-                                                       Character.valueOf('h'),
-                                                       Character.valueOf('i'),
-                                                       Character.valueOf('j'),
-                                                       Character.valueOf('k'),
-                                                       Character.valueOf('l'),
-                                                       Character.valueOf('m') }),
-                     Arrays.toString(chars.stream().map(ch -> ch.getThisCharacter()).sorted().toArray()));
-
-        assertEquals(101 + 102 + 103 + 104 + 105 + 106 + 107 + 108 + 109,
-                     chars.stream().mapToInt(AsciiCharacter::getNumericValue).sum());
-
-        Set<String> sorted = new TreeSet<>();
-        chars.forEach(ch -> sorted.add(ch.getHexadecimal()));
-        assertEquals(new TreeSet<>(Set.of("65", "66", "67", "68", "69", "6a", "6b", "6c", "6d")),
-                     sorted);
-
-        Streamable<AsciiCharacter> empty = characters.findByNumericValueLessThanEqualAndNumericValueGreaterThanEqual(115, 120);
-        assertEquals(false, empty.iterator().hasNext());
-        assertEquals(0L, empty.stream().count());
-    }
+//
+//    @Assertion(id = "133", strategy = "Use a repository method that returns Streamable and verify the results.")
+//    public void testStreamable() {
+//        Streamable<AsciiCharacter> chars = characters.findByNumericValueLessThanEqualAndNumericValueGreaterThanEqual(109, 101);
+//
+//        assertEquals(Arrays.toString(new Character[] { Character.valueOf('e'),
+//                                                       Character.valueOf('f'),
+//                                                       Character.valueOf('g'),
+//                                                       Character.valueOf('h'),
+//                                                       Character.valueOf('i'),
+//                                                       Character.valueOf('j'),
+//                                                       Character.valueOf('k'),
+//                                                       Character.valueOf('l'),
+//                                                       Character.valueOf('m') }),
+//                     Arrays.toString(chars.stream().map(ch -> ch.getThisCharacter()).sorted().toArray()));
+//
+//        assertEquals(101 + 102 + 103 + 104 + 105 + 106 + 107 + 108 + 109,
+//                     chars.stream().mapToInt(AsciiCharacter::getNumericValue).sum());
+//
+//        Set<String> sorted = new TreeSet<>();
+//        chars.forEach(ch -> sorted.add(ch.getHexadecimal()));
+//        assertEquals(new TreeSet<>(Set.of("65", "66", "67", "68", "69", "6a", "6b", "6c", "6d")),
+//                     sorted);
+//
+//        Streamable<AsciiCharacter> empty = characters.findByNumericValueLessThanEqualAndNumericValueGreaterThanEqual(115, 120);
+//        assertEquals(false, empty.iterator().hasNext());
+//        assertEquals(0L, empty.stream().count());
+//    }
 
     @Assertion(id = "133",
                strategy = "Request the third Page of 10 results, expecting to find all 10. " +
@@ -1532,26 +1528,26 @@ public class EntityTests {
                strategy = "Request the third Slice of 5 results, expecting to find all 5. "
                        +  "Request the next Slice via nextPageRequest, expecting page number 4 and another 5 results.")
     public void testThirdAndFourthSlicesOf5() {
-        PageRequest<NaturalNumber> third5 = PageRequest.of(NaturalNumber.class).page(3).size(5).sortBy(Sort.desc("id"));
-        Slice<NaturalNumber> slice = numbers.findByNumTypeAndFloorOfSquareRootLessThanEqual(NumberType.PRIME, 8L,
+        PageRequest<NaturalNumber> third5 = PageRequest.of(NaturalNumber.class).page(3).size(5).sortBy(Sort.desc("id")).withoutTotal();
+        Page<NaturalNumber> page = numbers.findByNumTypeAndFloorOfSquareRootLessThanEqual(NumberType.PRIME, 8L,
                 third5);
 
-        assertEquals(3, slice.pageRequest().page());
-        assertEquals(5, slice.numberOfElements());
+        assertEquals(3, page.pageRequest().page());
+        assertEquals(5, page.numberOfElements());
 
         assertEquals(Arrays.toString(new Long[] { 37L, 31L, 29L, 23L, 19L }),
-                Arrays.toString(slice.stream().map(number -> number.getId()).toArray()));
+                Arrays.toString(page.stream().map(number -> number.getId()).toArray()));
 
-        assertEquals(true, slice.hasNext());
-        PageRequest<NaturalNumber> fourth5 = slice.nextPageRequest();
+        assertEquals(true, page.hasNext());
+        PageRequest<NaturalNumber> fourth5 = page.nextPageRequest();
 
-        slice = numbers.findByNumTypeAndFloorOfSquareRootLessThanEqual(NumberType.PRIME, 8L, fourth5);
+        page = numbers.findByNumTypeAndFloorOfSquareRootLessThanEqual(NumberType.PRIME, 8L, fourth5);
 
-        assertEquals(4, slice.pageRequest().page());
-        assertEquals(5, slice.numberOfElements());
+        assertEquals(4, page.pageRequest().page());
+        assertEquals(5, page.numberOfElements());
 
         assertEquals(Arrays.toString(new Long[] { 17L, 13L, 11L, 7L, 5L }),
-                Arrays.toString(slice.stream().map(number -> number.getId()).toArray()));
+                Arrays.toString(page.stream().map(number -> number.getId()).toArray()));
     }
 
     @Assertion(id = "133", strategy = "Use a repository method with the True keyword.")
diff --git a/tck/src/main/java/ee/jakarta/tck/data/standalone/persistence/PersistenceEntityTests.java b/tck/src/main/java/ee/jakarta/tck/data/standalone/persistence/PersistenceEntityTests.java
index b190b87a..3790b39d 100644
--- a/tck/src/main/java/ee/jakarta/tck/data/standalone/persistence/PersistenceEntityTests.java
+++ b/tck/src/main/java/ee/jakarta/tck/data/standalone/persistence/PersistenceEntityTests.java
@@ -41,7 +41,6 @@ import ee.jakarta.tck.data.standalone.persistence.Product.Department;
 
 import jakarta.data.Order;
 import jakarta.data.Sort;
-import jakarta.data.Streamable;
 import jakarta.data.exceptions.EntityExistsException;
 import jakarta.data.exceptions.MappingException;
 import jakarta.data.exceptions.OptimisticLockingFailureException;
@@ -314,7 +313,7 @@ public class PersistenceEntityTests {
         catalog.save(Product.of("basketball", 14.88, "TEST-PROD-76", Department.SPORTING_GOODS));
         catalog.save(Product.of("baseball cap", 12.99, "TEST-PROD-77", Department.SPORTING_GOODS, Department.CLOTHING));
 
-        Streamable<Product> found = catalog.findByDepartmentCountAndPriceBelow(2, 100.0);
+        List<Product> found = catalog.findByDepartmentCountAndPriceBelow(2, 100.0);
 
         assertEquals(List.of("baseball cap", "toothpaste"),
                      found.stream().map(Product::getName).collect(Collectors.toList()));