Does Hibernate perform in-memory pagination when using Join Fetch?
0 reputation · 20 Jul 2020, 22:19 UTC
0 reputation · 20 Jul 2020, 22:19 UTC
Hibernate provides pagination through setFirstResult() and setMaxResults(), which typically delegate the limit and offset logic to the database dialect to ensure efficiency.
However, a specific architectural challenge arises when combining these pagination methods with JOIN FETCH clauses. Because fetch joins can produce duplicate root entities in the result set, there is a risk that the ORM cannot accurately translate the requested limit into a SQL clause without risking data loss or incorrect entity counts.
When this occurs, Hibernate may bypass the database-level limit and load the entire result set into memory to perform deduplication before applying the pagination.
LIMIT or OFFSET clause?hibernate.show_sql?34775 reputation · 21 Jul 2020, 06:46 UTC
When a JPQL/HQL query contains a JOIN FETCH clause and the query is paginated with setFirstResult() / setMaxResults(), Hibernate does **not** delegate the limit/offset to the database. Instead, it loads all rows that satisfy the join, materialises them into memory, removes duplicate root entities, and then applies the pagination in Java. This behaviour is the same for Hibernate 5.2 and newer; earlier versions behaved identically.
JOIN FETCH that expands the root entity’s associations.setFirstResult/setMaxResults).LIMIT/OFFSET because the join may produce duplicate root rows.FetchJoinUtil detects that the join multiplicity > 1 and chooses the “in‑memory paging” strategy.Enable SQL logging and statistics:
hibernate.show_sql=true
hibernate.format_sql=true
hibernate.generate_statistics=true
Run the paginated query. In the log you should see:
LIMIT or OFFSET clause.EntityManager: fetched 1200 rows for root entity X (actual wording may vary).Result size after paging: 20.If you see a LIMIT/OFFSET in the SQL, the query is not using in‑memory paging.
JOIN FETCH on paginated queries. Use separate queries or @BatchSize / @Fetch(FetchMode.SUBSELECT) to load associations lazily after pagination.maxResults small and monitor memory usage.LIMIT/OFFSET and manual mapping if deterministic paging is required.To refine this advice, knowing the Hibernate version you run (e.g., 5.6.x vs 6.0.x) can help, as newer releases may introduce subtle optimisations.
Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 21 Jul 2020, 06:21 UTC
To supplement the logging verification, it is important to look for a specific warning code in the application logs. When Hibernate detects that it must perform pagination in memory due to a JOIN FETCH on a collection, it typically emits the warning HHH000104.
The log entry usually follows this pattern:
WARN org.hibernate.engine.spi.SessionFactoryImplementor - firstResult/maxResults specified with collection fetch; applying in memory!
Because this warning is issued at the WARN level, it may be suppressed depending on your logging configuration (e.g., Logback or Log4j2). If you see a query without LIMIT or OFFSET clauses but do not see this warning, verify that your org.hibernate logger is set to WARN or INFO. This is the most reliable way to confirm the behavior without manually calculating row counts from the JDBC result set.