Skip to content

Commit efc041c

Browse files
committed
HHH-11453 - Documentation: explain/state find() and Query on a single entity can behave differently
1 parent a38ea75 commit efc041c

File tree

4 files changed

+191
-3
lines changed

4 files changed

+191
-3
lines changed

documentation/src/main/asciidoc/userguide/chapters/fetching/Fetching.adoc

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,67 @@ _dynamic_ (sometimes referred to as runtime)::
4545
HQL/JPQL::: and both Hibernate and JPA Criteria queries have the ability to specify fetching, specific to said query.
4646
entity graphs::: Starting in Hibernate 4.2 (JPA 2.1) this is also an option.
4747

48+
[[fetching-direct-vs-query]]
49+
=== Direct fetching vs entity queries
50+
51+
To see the difference between direct fetching and entity queries in regard to eagerly fetched associations, consider the following entities:
52+
53+
[[fetching-direct-vs-query-domain-model-example]]
54+
.Domain model
55+
====
56+
[source, JAVA, indent=0]
57+
----
58+
include::{sourcedir}/DirectVsQueryFetchingTest.java[tags=fetching-direct-vs-query-domain-model-example]
59+
----
60+
====
61+
62+
The `Employee` entity has a `@ManyToOne` association to a `Department` which is fetched eagerly.
63+
64+
When issuing a direct entity fetch, Hibernate executed the following SQL query:
65+
66+
[[fetching-direct-vs-query-direct-fetching-example]]
67+
.Direct fetching example
68+
====
69+
[source, JAVA, indent=0]
70+
----
71+
include::{sourcedir}/DirectVsQueryFetchingTest.java[tags=fetching-direct-vs-query-direct-fetching-example]
72+
----
73+
74+
[source, SQL, indent=0]
75+
----
76+
include::{extrasdir}/fetching-direct-vs-query-direct-fetching-example.sql[]
77+
----
78+
====
79+
80+
The `LEFT JOIN` clause is added to the generated SQL query because this association is required to be fetched eagerly.
81+
82+
On the other hand, if you are using an entity query that does not contain a `JOIN FETCH` directive to the `Department` association:
83+
84+
[[fetching-direct-vs-query-entity-query-example]]
85+
.Entity query fetching example
86+
====
87+
[source, JAVA, indent=0]
88+
----
89+
include::{sourcedir}/DirectVsQueryFetchingTest.java[tags=fetching-direct-vs-query-entity-query-example]
90+
----
91+
92+
[source, SQL, indent=0]
93+
----
94+
include::{extrasdir}/fetching-direct-vs-query-entity-query-example.sql[]
95+
----
96+
====
97+
98+
Hibernate uses a secondary select instead. This is because the entity query fetch policy cannot be overridden,
99+
so Hibernate requires a secondary select to ensure that the EAGER association is fetched prior to returning the result to the user.
100+
101+
[IMPORTANT]
102+
====
103+
If you forget to JOIN FETCH all EAGER associations, Hibernate is going to issue a secondary select for each and every one of those
104+
which, in turn, can lean to N+1 query issues.
105+
106+
For this reason, you should prefer LAZY associations.
107+
====
108+
48109
[[fetching-strategies]]
49110
=== Applying fetch strategies
50111

@@ -82,7 +143,7 @@ include::{sourcedir}/FetchingTest.java[tags=fetching-strategies-no-fetching-exam
82143
====
83144

84145
In this example, the application gets the `Employee` data.
85-
However, because all associations from `Employee `are declared as LAZY (JPA defines the default for collections as LAZY) no other data is fetched.
146+
However, because all associations from `Employee` are declared as LAZY (JPA defines the default for collections as LAZY) no other data is fetched.
86147

87148
If the login process does not need access to the `Employee` information specifically, another fetching optimization here would be to limit the width of the query results.
88149

@@ -99,7 +160,7 @@ include::{sourcedir}/FetchingTest.java[tags=fetching-strategies-no-fetching-scal
99160
=== Dynamic fetching via queries
100161

101162
For the second use case, consider a screen displaying the `Projects` for an `Employee`.
102-
Certainly access to the `Employee `is needed, as is the collection of `Projects` for that Employee. Information about `Departments`, other `Employees` or other `Projects` is not needed.
163+
Certainly access to the `Employee` is needed, as is the collection of `Projects` for that Employee. Information about `Departments`, other `Employees` or other `Projects` is not needed.
103164

104165
[[fetching-strategies-dynamic-fetching-jpql-example]]
105166
.Dynamic JPQL fetching example
@@ -119,7 +180,7 @@ include::{sourcedir}/FetchingTest.java[tags=fetching-strategies-dynamic-fetching
119180
----
120181
====
121182

122-
In this example we have an `Employee `and their `Projects` loaded in a single query shown both as an HQL query and a JPA Criteria query.
183+
In this example we have an `Employee` and their `Projects` loaded in a single query shown both as an HQL query and a JPA Criteria query.
123184
In both cases, this resolves to exactly one database query to get all that information.
124185

125186
[[fetching-strategies-dynamic-fetching-entity-graph]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
select
2+
e.id as id1_1_0_,
3+
e.department_id as departme3_1_0_,
4+
e.username as username2_1_0_,
5+
d.id as id1_0_1_
6+
from
7+
Employee e
8+
left outer join
9+
Department d
10+
on e.department_id=d.id
11+
where
12+
e.id = 1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
select
2+
e.id as id1_1_,
3+
e.department_id as departme3_1_,
4+
e.username as username2_1_
5+
from
6+
Employee e
7+
where
8+
e.id = 1
9+
10+
select
11+
d.id as id1_0_0_
12+
from
13+
Department d
14+
where
15+
d.id = 1
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.userguide.fetching;
8+
9+
import javax.persistence.Entity;
10+
import javax.persistence.FetchType;
11+
import javax.persistence.Id;
12+
import javax.persistence.ManyToOne;
13+
14+
import org.hibernate.annotations.NaturalId;
15+
import org.hibernate.dialect.H2Dialect;
16+
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
17+
18+
import org.hibernate.testing.RequiresDialect;
19+
import org.junit.Test;
20+
21+
import org.jboss.logging.Logger;
22+
23+
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
24+
25+
/**
26+
* @author Vlad Mihalcea
27+
*/
28+
@RequiresDialect(H2Dialect.class)
29+
public class DirectVsQueryFetchingTest extends BaseEntityManagerFunctionalTestCase {
30+
31+
private static final Logger log = Logger.getLogger( DirectVsQueryFetchingTest.class );
32+
33+
@Override
34+
protected Class<?>[] getAnnotatedClasses() {
35+
return new Class<?>[] {
36+
Department.class,
37+
Employee.class,
38+
};
39+
}
40+
41+
@Test
42+
public void test() {
43+
doInJPA( this::entityManagerFactory, entityManager -> {
44+
Department department = new Department();
45+
department.id = 1L;
46+
entityManager.persist( department );
47+
48+
Employee employee1 = new Employee();
49+
employee1.id = 1L;
50+
employee1.username = "user1";
51+
employee1.department = department;
52+
entityManager.persist( employee1 );
53+
54+
} );
55+
56+
doInJPA( this::entityManagerFactory, entityManager -> {
57+
//tag::fetching-direct-vs-query-direct-fetching-example[]
58+
Employee employee = entityManager.find( Employee.class, 1L );
59+
//end::fetching-direct-vs-query-direct-fetching-example[]
60+
} );
61+
62+
doInJPA( this::entityManagerFactory, entityManager -> {
63+
//tag::fetching-direct-vs-query-entity-query-example[]
64+
Employee employee = entityManager.createQuery(
65+
"select e " +
66+
"from Employee e " +
67+
"where e.id = :id", Employee.class)
68+
.setParameter( "id", 1L )
69+
.getSingleResult();
70+
//end::fetching-direct-vs-query-entity-query-example[]
71+
} );
72+
}
73+
74+
//tag::fetching-direct-vs-query-domain-model-example[]
75+
@Entity(name = "Department")
76+
public static class Department {
77+
78+
@Id
79+
private Long id;
80+
81+
//Getters and setters omitted for brevity
82+
}
83+
84+
//tag::fetching-direct-vs-query-domain-model-example[]
85+
@Entity(name = "Employee")
86+
public static class Employee {
87+
88+
@Id
89+
private Long id;
90+
91+
@NaturalId
92+
private String username;
93+
94+
@ManyToOne(fetch = FetchType.EAGER)
95+
private Department department;
96+
97+
//Getters and setters omitted for brevity
98+
}
99+
//end::fetching-direct-vs-query-domain-model-example[]
100+
}

0 commit comments

Comments
 (0)