java 43 lines · 7 steps

Streaming large JDBC result sets safely

A JDBC query streams rows one at a time via fetch size and a callback instead of loading everything into memory at once.

Explained by highlit
1public List<OrderSummary> streamRecentOrders(LocalDateTime since, Consumer<OrderSummary> handler) {
2 String sql = """
3 SELECT id, customer_id, total_cents, status, created_at
4 FROM orders
5 WHERE created_at >= ?
6 ORDER BY created_at
7 """;
8 
9 List<OrderSummary> processed = new ArrayList<>();
10 
11 try (Connection conn = dataSource.getConnection()) {
12 conn.setAutoCommit(false);
13 
14 try (PreparedStatement ps = conn.prepareStatement(
15 sql,
16 ResultSet.TYPE_FORWARD_ONLY,
17 ResultSet.CONCUR_READ_ONLY)) {
18 
19 ps.setFetchSize(500);
20 ps.setTimestamp(1, Timestamp.valueOf(since));
21 
22 try (ResultSet rs = ps.executeQuery()) {
23 while (rs.next()) {
24 OrderSummary order = new OrderSummary(
25 rs.getLong("id"),
26 rs.getLong("customer_id"),
27 rs.getLong("total_cents"),
28 OrderStatus.valueOf(rs.getString("status")),
29 rs.getTimestamp("created_at").toLocalDateTime());
30 
31 handler.accept(order);
32 processed.add(order);
33 }
34 }
35 }
36 
37 conn.commit();
38 } catch (SQLException e) {
39 throw new DataAccessException("Failed to stream recent orders", e);
40 }
41 
42 return processed;
43}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Setting a fetch size lets the driver pull rows in batches instead of buffering the entire result set in memory.
  2. 2Nested try-with-resources guarantees every JDBC resource closes in the right order even when errors occur.
  3. 3A Consumer callback lets callers process each row as it arrives, decoupling iteration from what happens per row.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Streaming large JDBC result sets safely — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code