From 60ad64b2a660c44b29079885ea5469a9e943d87c Mon Sep 17 00:00:00 2001 From: Gavin King Date: Sat, 8 Jan 2022 11:58:44 +0100 Subject: [PATCH] update explanation of limit/fetch join problem for H6 --- .../chapters/query/hql/QueryLanguage.adoc | 39 ++++++++++++++++--- .../org/hibernate/userguide/hql/HQLTest.java | 5 +-- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/documentation/src/main/asciidoc/userguide/chapters/query/hql/QueryLanguage.adoc b/documentation/src/main/asciidoc/userguide/chapters/query/hql/QueryLanguage.adoc index df40c0ae19..3b3964d62c 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/query/hql/QueryLanguage.adoc +++ b/documentation/src/main/asciidoc/userguide/chapters/query/hql/QueryLanguage.adoc @@ -2105,17 +2105,17 @@ include::{sourcedir}/HQLTest.java[tags=hql-limit-example] ---- ==== -These are well-defined limits: the number of results returned by `getMaxResults()` will be limited to 50, as promised. +These are well-defined limits: the number of results returned by the database will be limited to 50, as promised. But not every query is quite so well-behaved. [NOTE] ==== _Limiting_ certainly _isn't_ a well-defined relational operation, and must be used with care. -A limit can easily break the semantics of certain other features of HQL, including <>. +In particular, limits don't play well with <>. ==== -This next query is accepted by HQL, but the last `Phone` in the result list might have an incomplete collection of phones: +This next query is accepted by HQL, and no more than 50 results are returned by `getResultList()`, just as expected: [[hql-bad-limit-example]] //.Order by example @@ -2126,7 +2126,36 @@ include::{sourcedir}/HQLTest.java[tags=hql-bad-limit-example] ---- ==== -The `limit 50` here refers to the total number of rows returned from the database. -The final number of ``Phone``s returned by `getResultList()` will be fewer, after Hibernate eliminates duplicate ``Phone``s from the result list. +However, if you log the SQL executed by Hibernate, you'll notice something wrong: + +[source, SQL, indent=0] +---- + select + p1_0.id, + c1_0.phone_id, + c1_0.calls_ORDER, + c1_0.id, + c1_0.duration, + c1_0.payment_id, + c1_0.call_timestamp, + p1_0.phone_number, + p1_0.person_id, + p1_0.phone_type + from + Phone p1_0 + join + phone_call c1_0 + on p1_0.id=c1_0.phone_id + order by 1 +---- + +What happened to the `limit` clause? + +[IMPORTANT] +==== +When limits or pagination are combined with `fetch join`, Hibernate must retrieve all matching results from the database and _apply the limit in memory_! + +This _almost certainly_ isn't the behavior you were hoping for, and in general will exhibit _terrible_ performance characteristics. +==== In the next chapter we'll see a completely different way to write queries in Hibernate. diff --git a/documentation/src/test/java/org/hibernate/userguide/hql/HQLTest.java b/documentation/src/test/java/org/hibernate/userguide/hql/HQLTest.java index 9cfb2ca30f..91011d3efc 100644 --- a/documentation/src/test/java/org/hibernate/userguide/hql/HQLTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/hql/HQLTest.java @@ -2939,15 +2939,12 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase { doInJPA(this::entityManagerFactory, entityManager -> { //tag::hql-bad-limit-example[] - // don't do this! + // don't do this! join fetch should not be used with limit List wrongCalls = entityManager.createQuery( "select p " + "from Phone p " + - // join fetch should not be used with limit "join fetch p.calls " + - // but if you insist, at least sort by the collection owner "order by p " + - // this won't be the final number of results! "limit 50", Phone.class) .getResultList();