HHH-11453 - Documentation: explain/state find() and Query on a single entity can behave differently
This commit is contained in:
parent
a38ea758e3
commit
efc041c9e6
|
@ -45,6 +45,67 @@ _dynamic_ (sometimes referred to as runtime)::
|
|||
HQL/JPQL::: and both Hibernate and JPA Criteria queries have the ability to specify fetching, specific to said query.
|
||||
entity graphs::: Starting in Hibernate 4.2 (JPA 2.1) this is also an option.
|
||||
|
||||
[[fetching-direct-vs-query]]
|
||||
=== Direct fetching vs entity queries
|
||||
|
||||
To see the difference between direct fetching and entity queries in regard to eagerly fetched associations, consider the following entities:
|
||||
|
||||
[[fetching-direct-vs-query-domain-model-example]]
|
||||
.Domain model
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/DirectVsQueryFetchingTest.java[tags=fetching-direct-vs-query-domain-model-example]
|
||||
----
|
||||
====
|
||||
|
||||
The `Employee` entity has a `@ManyToOne` association to a `Department` which is fetched eagerly.
|
||||
|
||||
When issuing a direct entity fetch, Hibernate executed the following SQL query:
|
||||
|
||||
[[fetching-direct-vs-query-direct-fetching-example]]
|
||||
.Direct fetching example
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/DirectVsQueryFetchingTest.java[tags=fetching-direct-vs-query-direct-fetching-example]
|
||||
----
|
||||
|
||||
[source, SQL, indent=0]
|
||||
----
|
||||
include::{extrasdir}/fetching-direct-vs-query-direct-fetching-example.sql[]
|
||||
----
|
||||
====
|
||||
|
||||
The `LEFT JOIN` clause is added to the generated SQL query because this association is required to be fetched eagerly.
|
||||
|
||||
On the other hand, if you are using an entity query that does not contain a `JOIN FETCH` directive to the `Department` association:
|
||||
|
||||
[[fetching-direct-vs-query-entity-query-example]]
|
||||
.Entity query fetching example
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/DirectVsQueryFetchingTest.java[tags=fetching-direct-vs-query-entity-query-example]
|
||||
----
|
||||
|
||||
[source, SQL, indent=0]
|
||||
----
|
||||
include::{extrasdir}/fetching-direct-vs-query-entity-query-example.sql[]
|
||||
----
|
||||
====
|
||||
|
||||
Hibernate uses a secondary select instead. This is because the entity query fetch policy cannot be overridden,
|
||||
so Hibernate requires a secondary select to ensure that the EAGER association is fetched prior to returning the result to the user.
|
||||
|
||||
[IMPORTANT]
|
||||
====
|
||||
If you forget to JOIN FETCH all EAGER associations, Hibernate is going to issue a secondary select for each and every one of those
|
||||
which, in turn, can lean to N+1 query issues.
|
||||
|
||||
For this reason, you should prefer LAZY associations.
|
||||
====
|
||||
|
||||
[[fetching-strategies]]
|
||||
=== Applying fetch strategies
|
||||
|
||||
|
@ -82,7 +143,7 @@ include::{sourcedir}/FetchingTest.java[tags=fetching-strategies-no-fetching-exam
|
|||
====
|
||||
|
||||
In this example, the application gets the `Employee` data.
|
||||
However, because all associations from `Employee `are declared as LAZY (JPA defines the default for collections as LAZY) no other data is fetched.
|
||||
However, because all associations from `Employee` are declared as LAZY (JPA defines the default for collections as LAZY) no other data is fetched.
|
||||
|
||||
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.
|
||||
|
||||
|
@ -99,7 +160,7 @@ include::{sourcedir}/FetchingTest.java[tags=fetching-strategies-no-fetching-scal
|
|||
=== Dynamic fetching via queries
|
||||
|
||||
For the second use case, consider a screen displaying the `Projects` for an `Employee`.
|
||||
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.
|
||||
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.
|
||||
|
||||
[[fetching-strategies-dynamic-fetching-jpql-example]]
|
||||
.Dynamic JPQL fetching example
|
||||
|
@ -119,7 +180,7 @@ include::{sourcedir}/FetchingTest.java[tags=fetching-strategies-dynamic-fetching
|
|||
----
|
||||
====
|
||||
|
||||
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.
|
||||
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.
|
||||
In both cases, this resolves to exactly one database query to get all that information.
|
||||
|
||||
[[fetching-strategies-dynamic-fetching-entity-graph]]
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
select
|
||||
e.id as id1_1_0_,
|
||||
e.department_id as departme3_1_0_,
|
||||
e.username as username2_1_0_,
|
||||
d.id as id1_0_1_
|
||||
from
|
||||
Employee e
|
||||
left outer join
|
||||
Department d
|
||||
on e.department_id=d.id
|
||||
where
|
||||
e.id = 1
|
|
@ -0,0 +1,15 @@
|
|||
select
|
||||
e.id as id1_1_,
|
||||
e.department_id as departme3_1_,
|
||||
e.username as username2_1_
|
||||
from
|
||||
Employee e
|
||||
where
|
||||
e.id = 1
|
||||
|
||||
select
|
||||
d.id as id1_0_0_
|
||||
from
|
||||
Department d
|
||||
where
|
||||
d.id = 1
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.userguide.fetching;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@RequiresDialect(H2Dialect.class)
|
||||
public class DirectVsQueryFetchingTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
private static final Logger log = Logger.getLogger( DirectVsQueryFetchingTest.class );
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Department.class,
|
||||
Employee.class,
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Department department = new Department();
|
||||
department.id = 1L;
|
||||
entityManager.persist( department );
|
||||
|
||||
Employee employee1 = new Employee();
|
||||
employee1.id = 1L;
|
||||
employee1.username = "user1";
|
||||
employee1.department = department;
|
||||
entityManager.persist( employee1 );
|
||||
|
||||
} );
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
//tag::fetching-direct-vs-query-direct-fetching-example[]
|
||||
Employee employee = entityManager.find( Employee.class, 1L );
|
||||
//end::fetching-direct-vs-query-direct-fetching-example[]
|
||||
} );
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
//tag::fetching-direct-vs-query-entity-query-example[]
|
||||
Employee employee = entityManager.createQuery(
|
||||
"select e " +
|
||||
"from Employee e " +
|
||||
"where e.id = :id", Employee.class)
|
||||
.setParameter( "id", 1L )
|
||||
.getSingleResult();
|
||||
//end::fetching-direct-vs-query-entity-query-example[]
|
||||
} );
|
||||
}
|
||||
|
||||
//tag::fetching-direct-vs-query-domain-model-example[]
|
||||
@Entity(name = "Department")
|
||||
public static class Department {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
//Getters and setters omitted for brevity
|
||||
}
|
||||
|
||||
//tag::fetching-direct-vs-query-domain-model-example[]
|
||||
@Entity(name = "Employee")
|
||||
public static class Employee {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@NaturalId
|
||||
private String username;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
private Department department;
|
||||
|
||||
//Getters and setters omitted for brevity
|
||||
}
|
||||
//end::fetching-direct-vs-query-domain-model-example[]
|
||||
}
|
Loading…
Reference in New Issue