Does Hibernate 6 offer a native keyset pagination API compatible with Spring Data JPA repositories?
0 reputation · 23 Sept 2021, 03:59 UTC
0 reputation · 23 Sept 2021, 03:59 UTC
Implement pagination over a table with millions of rows while avoiding the performance degradation of OFFSET‑based queries and still integrating with Spring Data JPA repository methods.
Hibernate 6 supports scrolling and custom SQL fragments that can express a seek‑method (WHERE id > :lastId ORDER BY id LIMIT :size), but there is no first‑class KeysetPageable in Spring Data JPA as of the latest release. The application already uses Pageable‑based repository signatures, and changing them would affect many callers. It is unclear whether Hibernate’s low‑level APIs can be wrapped to satisfy the Pageable contract without losing the ability to request a total count or to support arbitrary sort orders.
35275 reputation · 23 Sept 2021, 04:26 UTC
No. Hibernate 6 does not expose a first-class keyset-pagination abstraction that plugs into standard Spring Data JPA Pageable or Page<T> repository methods. Pageable is offset-based by contract, and Spring Data translates getOffset() into setFirstResult() on the underlying query.
Confirmed: Hibernate 6 can express keyset (seek-method) queries directly through HQL, the Criteria API, or native SQL by combining a unique ordering column with tiebreakers, a WHERE predicate such as id > :lastId, and setMaxResults(). This is a low-level capability, not a Spring Data integration.
Likely explanation: The assumption that Hibernate 6 added native keyset support often comes from its improved scroll and streaming APIs. Those APIs operate on result sets and sessions, not on the Pageable contract. They do not automatically satisfy Page<T> semantics such as total element counts.
If you must keep existing Pageable-based call sites, do not override getOffset() to smuggle a cursor value; Spring Data will still pass that value to setFirstResult(). Instead, add a custom repository fragment that accepts a cursor or a dedicated parameter and builds the keyset predicate itself.
UserRepositoryCustom, with a method like Slice<User> findAfterCursor(Long lastId, int limit).EntityManager or StatelessSession to create a query with WHERE id > :lastId ORDER BY id ASC and setMaxResults(limit).SliceImpl (or Window if your Spring Data JPA version supports it) so callers can detect whether more rows exist without a total count.Recent Spring Data JPA releases introduced ScrollPosition and Window<T> return types for repository methods. If your version supports them, a method such as Window<User> findFirst10ByOrderById(ScrollPosition position) can express keyset navigation without Pageable. Verify this in your build before relying on it; the exact signatures have changed across releases.
(a, b) > (:a, :b) or equivalent).pg_class.reltuples) are stale and do not satisfy Page<T> semantics such as getTotalElements() or isLast(). Use them only for UI hints, not for pagination logic.Before rolling this out, confirm the generated SQL uses the keyset predicate and LIMIT rather than OFFSET. Run EXPLAIN on the query or enable Hibernate SQL logging. Then test with concurrent inserts and deletes around a captured cursor to ensure no rows are skipped or repeated under your transaction isolation level.
Do any existing callers depend on Page<T>.getTotalElements() or isLast()? If yes, those endpoints should remain on Pageable/offset or move to a dedicated cursor API; if no, a Slice or Window return type is the safer path.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.