java 52 lines · 8 steps

How keyset pagination works in Java

Page through orders using a composite cursor instead of OFFSET, keeping queries fast at any depth.

Explained by highlit
1public class KeysetPaginationRepository {
2 
3 private final DataSource dataSource;
4 
5 public KeysetPaginationRepository(DataSource dataSource) {
6 this.dataSource = dataSource;
7 }
8 
9 public Page<Order> fetchNextPage(Instant afterCreatedAt, long afterId, int limit) {
10 String sql = """
11 SELECT id, customer_email, total_cents, created_at
12 FROM orders
13 WHERE (created_at, id) < (?, ?)
14 ORDER BY created_at DESC, id DESC
15 LIMIT ?
16 """;
17 
18 List<Order> orders = new ArrayList<>(limit);
19 
20 try (Connection conn = dataSource.getConnection();
21 PreparedStatement ps = conn.prepareStatement(sql)) {
22 
23 ps.setTimestamp(1, Timestamp.from(afterCreatedAt));
24 ps.setLong(2, afterId);
25 ps.setInt(3, limit + 1);
26 
27 try (ResultSet rs = ps.executeQuery()) {
28 while (rs.next()) {
29 orders.add(new Order(
30 rs.getLong("id"),
31 rs.getString("customer_email"),
32 rs.getLong("total_cents"),
33 rs.getTimestamp("created_at").toInstant()
34 ));
35 }
36 }
37 } catch (SQLException e) {
38 throw new DataAccessException("Failed to page orders", e);
39 }
40 
41 boolean hasMore = orders.size() > limit;
42 if (hasMore) {
43 orders.remove(orders.size() - 1);
44 }
45 
46 Cursor next = orders.isEmpty()
47 ? null
48 : new Cursor(orders.get(orders.size() - 1).createdAt(), orders.get(orders.size() - 1).id());
49 
50 return new Page<>(orders, hasMore ? next : null);
51 }
52}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Keyset pagination filters by the last row's sort key rather than skipping rows, so query cost stays constant no matter how deep you page.
  2. 2A composite comparison like (created_at, id) < (?, ?) breaks ties deterministically when the primary sort column isn't unique.
  3. 3Fetching limit + 1 rows is a cheap trick to know whether another page exists without a separate count query.

Related explainers

Share this explainer

Here's the card — post it anywhere.

How keyset pagination works in Java — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code