Careeroza
Fetch Size and Streaming
What Fetch Size Controls
When a query returns many rows, the JDBC driver pulls them from the server in chunks. Fetch size is the chunk size. Too small means many round trips; too large means a lot of memory per query.
Example
Reading 100,000 order rows for a monthly sales report:
public class Main {
public static void main(String[] args) {
int rows = 100_000;
int[] fetchSizes = {10, 100, 1000, 10_000};
System.out.println("fetch size | round trips | rows held in driver memory");
for (int f : fetchSizes) {
int trips = (rows + f - 1) / f;
System.out.printf("%10d | %11d | %d%n", f, trips, f);
}
}
}Output
fetch size | round trips | rows held in driver memory
10 | 10000 | 10
100 | 1000 | 100
1000 | 100 | 1000
10000 | 10 | 10000Setting It
| Where | How |
|---|---|
| Globally | spring.jpa.properties.hibernate.jdbc.fetch_size=100 |
| Per query | @QueryHints(@QueryHint(name = "org.hibernate.fetchSize", value = "500")) |
| Oracle | driver default is only 10 rows: always raise it for reports |
| PostgreSQL | driver loads the whole result unless fetch size > 0 and auto-commit is off (inside a transaction) |
| MySQL | loads everything by default; use useCursorFetch=true in the URL plus a fetch size to stream |
Streaming Large Results
public interface OrderRepository extends JpaRepository<Order, Long> {
@QueryHints({
@QueryHint(name = "org.hibernate.fetchSize", value = "500"),
@QueryHint(name = "org.hibernate.readOnly", value = "true")
})
@Query("select o from Order o where o.createdAt >= :from and o.createdAt < :to")
Stream<Order> streamForReport(@Param("from") Instant from, @Param("to") Instant to);
}
@Transactional(readOnly = true) // the stream needs an open transaction
public void writeMonthlyCsv(Writer out, YearMonth month) {
try (Stream<Order> orders = orderRepository.streamForReport(start(month), end(month))) {
orders.forEach(o -> {
writeCsvLine(out, o);
entityManager.detach(o); // keep the persistence context small
});
}
}Note: Always close the stream (try-with-resources). An open stream keeps the JDBC connection and cursor busy.