Hibernate Pagination: Memory-side loading when using JOIN FETCH
26.5K reputation · 11 Dec 2021, 00:13 UTC
Hibernate utilizes setFirstResult() and setMaxResults() to implement pagination, typically delegating the offset and limit logic to the database via the configured SQL dialect.
A specific behavioral conflict occurs when JOIN FETCH is applied to a collection within a paginated query. Because joining a one-to-many relationship can produce duplicate parent rows in the result set, the ORM cannot reliably calculate the correct row offset at the database level without risking data loss or incorrect page sizes.
In such scenarios, Hibernate may bypass the database-level pagination and load the entire result set into memory to perform the filtering manually. This behavior creates a significant risk of OutOfMemoryError when dealing with large datasets.
Technical Uncertainties
- Under what specific version conditions does Hibernate prioritize memory-side pagination over throwing an exception when a fetch join is detected?
- What is the precise threshold or configuration that determines when the persistence context triggers a warning regarding in-memory pagination?
1 answer
1 question comment
Use comments to ask for clarification. Post a solution as an answer.
26,525 reputation · 11 Dec 2021, 09:53 UTC
To clarify the detection of this behavior, Hibernate typically emits a specific warning when it is forced to perform pagination in memory. In most versions (including Hibernate 5.x and 6.x), look for the log message HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!
It is important to note that this risk is exclusive to collection-valued associations (One-to-Many or Many-to-Many). Using JOIN FETCH on a @ManyToOne or @OneToOne relationship does not trigger in-memory pagination because these joins do not multiply the number of root entity rows in the result set, allowing the database to safely apply LIMIT and OFFSET.
For verification, if you see this warning in your logs, you can confirm the performance hit by checking if the generated SQL lacks the pagination keywords despite setMaxResults() being called.