mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-08 12:14:47 +00:00
HHH-11186 - Add examples for all Hibernate annotations
Document more annotations: - @Fetch along with SELECT, SUBSELECT, and JOIN FetchMode
This commit is contained in:
parent
de346827d0
commit
cc1730fb01
@ -749,6 +749,8 @@ The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibern
|
|||||||
|
|
||||||
The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/Fetch.html[`@Fetch`] annotation is used to specify the Hibernate specific https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/FetchMode.html[`FetchMode`] (e.g. `JOIN`, `SELECT`, `SUBSELECT`) used for the current annotated association:
|
The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/Fetch.html[`@Fetch`] annotation is used to specify the Hibernate specific https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/FetchMode.html[`FetchMode`] (e.g. `JOIN`, `SELECT`, `SUBSELECT`) used for the current annotated association:
|
||||||
|
|
||||||
|
See the <<chapters/fetching/Fetching.adoc#fetching-fetch-annotation, `@Fetch` mapping>> section for more info.
|
||||||
|
|
||||||
[[annotations-hibernate-fetchprofile]]
|
[[annotations-hibernate-fetchprofile]]
|
||||||
==== `@FetchProfile`
|
==== `@FetchProfile`
|
||||||
|
|
||||||
|
@ -217,4 +217,138 @@ instead of 2 SQL statements, there would be 10 queries needed for fetching the `
|
|||||||
However, although `@BatchSize` is better than running into an N+1 query issue,
|
However, although `@BatchSize` is better than running into an N+1 query issue,
|
||||||
most of the time, a DTO projection or a `JOIN FETCH` is a much better alternative since
|
most of the time, a DTO projection or a `JOIN FETCH` is a much better alternative since
|
||||||
it allows you to fetch all the required data with a single query.
|
it allows you to fetch all the required data with a single query.
|
||||||
====
|
====
|
||||||
|
|
||||||
|
[[fetching-fetch-annotation]]
|
||||||
|
=== The `@Fetch` annotation mapping
|
||||||
|
|
||||||
|
Besides the `FetchType.LAZY` or `FetchType.EAGER` JPA annotations,
|
||||||
|
you can also use the Hibernate-specific `@Fetch` annotation that accepts one of the following `FetchMode`s:
|
||||||
|
|
||||||
|
SELECT::
|
||||||
|
Use a secondary select for each individual entity, collection, or join load.
|
||||||
|
JOIN::
|
||||||
|
Use an outer join to load the related entities, collections or joins.
|
||||||
|
SUBSELECT::
|
||||||
|
Available for collections only.
|
||||||
|
When accessing a non-initialized collection, this fetch mode will trigger loading all elements of all collections of the same role
|
||||||
|
for all owners associated with the persistence context using a single secondary select.
|
||||||
|
|
||||||
|
[[fetching-fetchmode-select]]
|
||||||
|
=== `FetchMode.SELECT`
|
||||||
|
|
||||||
|
To demonstrate how `FetchMode.SELECT` works, consider the following entity mapping:
|
||||||
|
|
||||||
|
[[fetching-strategies-fetch-mode-select-mapping-example]]
|
||||||
|
.`FetchMode.SELECT` mapping example
|
||||||
|
====
|
||||||
|
[source, JAVA, indent=0]
|
||||||
|
----
|
||||||
|
include::{sourcedir}/FetchModeSelectTest.java[tags=fetching-strategies-fetch-mode-select-mapping-example]
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
Considering there are multiple `Department` entities, each one having multiple `Employee` entities,
|
||||||
|
when executing the following test case, Hibernate fetches every uninitialized `Employee`
|
||||||
|
collection using a secondary `SELECT` statement upon accessing the child collection for the first time:
|
||||||
|
|
||||||
|
[[fetching-strategies-fetch-mode-select-example]]
|
||||||
|
.`FetchMode.SELECT` mapping example
|
||||||
|
====
|
||||||
|
[source, JAVA, indent=0]
|
||||||
|
----
|
||||||
|
include::{sourcedir}/FetchModeSelectTest.java[tags=fetching-strategies-fetch-mode-select-example]
|
||||||
|
----
|
||||||
|
|
||||||
|
[source, SQL, indent=0]
|
||||||
|
----
|
||||||
|
include::{extrasdir}/fetching-strategies-fetch-mode-select-example.sql[]
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
The more `Department` entities are fetched by the first query, the more secondary `SELECT` statements are executed to initialize the `employees` collections.
|
||||||
|
Therefore, `FetchMode.SELECT` can lead to N+1 query issues.
|
||||||
|
|
||||||
|
[[fetching-fetchmode-subselect]]
|
||||||
|
=== `FetchMode.SUBSELECT`
|
||||||
|
|
||||||
|
To demonstrate how `FetchMode.SUBSELECT` works, we are going to modify the <<fetching-strategies-fetch-mode-select-mapping-example>> to use
|
||||||
|
`FetchMode.SUBSELECT`:
|
||||||
|
|
||||||
|
[[fetching-strategies-fetch-mode-subselect-mapping-example]]
|
||||||
|
.`FetchMode.SUBSELECT` mapping example
|
||||||
|
====
|
||||||
|
[source, JAVA, indent=0]
|
||||||
|
----
|
||||||
|
include::{sourcedir}/FetchModeSubselectTest.java[tags=fetching-strategies-fetch-mode-subselect-mapping-example]
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
Now, we are going to fetch all `Department` entities that match a given filtering criteria
|
||||||
|
and then navigate their `employees` collections.
|
||||||
|
|
||||||
|
Hibernate is going to avoid the N+1 query issue by generating a single SQL statement to initialize all `employees` collections
|
||||||
|
for all `Department` entities that were previously fetched.
|
||||||
|
Instead of using passing all entity identifiers, Hibernate simply reruns the previous query that fetched the `Department` entities.
|
||||||
|
|
||||||
|
[[fetching-strategies-fetch-mode-subselect-example]]
|
||||||
|
.`FetchMode.SUBSELECT` mapping example
|
||||||
|
====
|
||||||
|
[source, JAVA, indent=0]
|
||||||
|
----
|
||||||
|
include::{sourcedir}/FetchModeSubselectTest.java[tags=fetching-strategies-fetch-mode-subselect-example]
|
||||||
|
----
|
||||||
|
|
||||||
|
[source, SQL, indent=0]
|
||||||
|
----
|
||||||
|
include::{extrasdir}/fetching-strategies-fetch-mode-subselect-example.sql[]
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
[[fetching-fetchmode-join]]
|
||||||
|
=== `FetchMode.JOIN`
|
||||||
|
|
||||||
|
To demonstrate how `FetchMode.JOIN` works, we are going to modify the <<fetching-strategies-fetch-mode-select-mapping-example>> to use
|
||||||
|
`FetchMode.JOIN` instead:
|
||||||
|
|
||||||
|
[[fetching-strategies-fetch-mode-join-mapping-example]]
|
||||||
|
.`FetchMode.JOIN` mapping example
|
||||||
|
====
|
||||||
|
[source, JAVA, indent=0]
|
||||||
|
----
|
||||||
|
include::{sourcedir}/FetchModeJoinTest.java[tags=fetching-strategies-fetch-mode-join-mapping-example]
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
Now, we are going to fetch one `Department` and navigate its `employees` collections.
|
||||||
|
|
||||||
|
[NOTE]
|
||||||
|
====
|
||||||
|
The reason why we are not using a JPQL query to fetch multiple `Department` entities is because
|
||||||
|
the `FetchMode.JOIN` strategy would be overridden by the query fetching directive.
|
||||||
|
|
||||||
|
To fetch multiple relationships with a JPQL query, the `JOIN FETCH` directive must be used instead.
|
||||||
|
|
||||||
|
Therefore, `FetchMode.JOIN` is useful for when entities are fetched directly, via their identifier or natural-id.
|
||||||
|
|
||||||
|
Also, the `FetchMode.JOIN` acts as a `FetchType.EAGER` strategy.
|
||||||
|
Even if we mark the association as `FetchType.LAZY`, the `FetchMode.JOIN` will load the association eagerly.
|
||||||
|
====
|
||||||
|
|
||||||
|
Hibernate is going to avoid the secondary query by issuing an OUTER JOIN for the `employees` collection.
|
||||||
|
|
||||||
|
[[fetching-strategies-fetch-mode-join-example]]
|
||||||
|
.`FetchMode.JOIN` mapping example
|
||||||
|
====
|
||||||
|
[source, JAVA, indent=0]
|
||||||
|
----
|
||||||
|
include::{sourcedir}/FetchModeJoinTest.java[tags=fetching-strategies-fetch-mode-join-example]
|
||||||
|
----
|
||||||
|
|
||||||
|
[source, SQL, indent=0]
|
||||||
|
----
|
||||||
|
include::{extrasdir}/fetching-strategies-fetch-mode-join-example.sql[]
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
This time, there was no secondary query because the child collection was loaded along with the parent entity.
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
SELECT
|
||||||
|
d.id as id1_0_0_,
|
||||||
|
e.department_id as departme3_1_1_,
|
||||||
|
e.id as id1_1_1_,
|
||||||
|
e.id as id1_1_2_,
|
||||||
|
e.department_id as departme3_1_2_,
|
||||||
|
e.username as username2_1_2_
|
||||||
|
FROM
|
||||||
|
Department d
|
||||||
|
LEFT OUTER JOIN
|
||||||
|
Employee e
|
||||||
|
on d.id = e.department_id
|
||||||
|
WHERE
|
||||||
|
d.id = 1
|
||||||
|
|
||||||
|
-- Fetched department: 1
|
@ -0,0 +1,28 @@
|
|||||||
|
SELECT
|
||||||
|
d.id as id1_0_
|
||||||
|
FROM
|
||||||
|
Department d
|
||||||
|
|
||||||
|
-- Fetched 2 Departments
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
e.department_id as departme3_1_0_,
|
||||||
|
e.id as id1_1_0_,
|
||||||
|
e.id as id1_1_1_,
|
||||||
|
e.department_id as departme3_1_1_,
|
||||||
|
e.username as username2_1_1_
|
||||||
|
FROM
|
||||||
|
Employee e
|
||||||
|
WHERE
|
||||||
|
e.department_id = 1
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
e.department_id as departme3_1_0_,
|
||||||
|
e.id as id1_1_0_,
|
||||||
|
e.id as id1_1_1_,
|
||||||
|
e.department_id as departme3_1_1_,
|
||||||
|
e.username as username2_1_1_
|
||||||
|
FROM
|
||||||
|
Employee e
|
||||||
|
WHERE
|
||||||
|
e.department_id = 2
|
@ -0,0 +1,26 @@
|
|||||||
|
SELECT
|
||||||
|
d.id as id1_0_
|
||||||
|
FROM
|
||||||
|
Department d
|
||||||
|
where
|
||||||
|
d.name like 'Department%'
|
||||||
|
|
||||||
|
-- Fetched 2 Departments
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
e.department_id as departme3_1_1_,
|
||||||
|
e.id as id1_1_1_,
|
||||||
|
e.id as id1_1_0_,
|
||||||
|
e.department_id as departme3_1_0_,
|
||||||
|
e.username as username2_1_0_
|
||||||
|
FROM
|
||||||
|
Employee e
|
||||||
|
WHERE
|
||||||
|
e.department_id in (
|
||||||
|
SELECT
|
||||||
|
fetchmodes0_.id
|
||||||
|
FROM
|
||||||
|
Department fetchmodes0_
|
||||||
|
WHERE
|
||||||
|
d.name like 'Department%'
|
||||||
|
)
|
@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* 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 java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.Fetch;
|
||||||
|
import org.hibernate.annotations.FetchMode;
|
||||||
|
import org.hibernate.annotations.NaturalId;
|
||||||
|
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Vlad Mihalcea
|
||||||
|
*/
|
||||||
|
public class FetchModeJoinTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
|
|
||||||
|
private static final Logger log = Logger.getLogger( FetchModeJoinTest.class );
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
|
return new Class<?>[] {
|
||||||
|
Department.class,
|
||||||
|
Employee.class,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||||
|
for ( long i = 0; i < 2; i++ ) {
|
||||||
|
Department department = new Department();
|
||||||
|
department.id = i + 1;
|
||||||
|
entityManager.persist( department );
|
||||||
|
|
||||||
|
for ( long j = 0; j < 3; j++ ) {
|
||||||
|
Employee employee1 = new Employee();
|
||||||
|
employee1.username = String.format( "user %d_%d", i, j );
|
||||||
|
employee1.department = department;
|
||||||
|
entityManager.persist( employee1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||||
|
//tag::fetching-strategies-fetch-mode-join-example[]
|
||||||
|
Department department = entityManager.find( Department.class, 1L );
|
||||||
|
|
||||||
|
log.infof( "Fetched department: %s", department.getId());
|
||||||
|
|
||||||
|
assertEquals( 3, department.getEmployees().size() );
|
||||||
|
//end::fetching-strategies-fetch-mode-join-example[]
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Department")
|
||||||
|
public static class Department {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
//tag::fetching-strategies-fetch-mode-join-mapping-example[]
|
||||||
|
@OneToMany(mappedBy = "department")
|
||||||
|
@Fetch(FetchMode.JOIN)
|
||||||
|
private List<Employee> employees = new ArrayList<>();
|
||||||
|
//end::fetching-strategies-fetch-mode-join-mapping-example[]
|
||||||
|
|
||||||
|
//Getters and setters omitted for brevity
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Employee> getEmployees() {
|
||||||
|
return employees;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmployees(List<Employee> employees) {
|
||||||
|
this.employees = employees;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Employee")
|
||||||
|
public static class Employee {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@NaturalId
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
private Department department;
|
||||||
|
|
||||||
|
//Getters and setters omitted for brevity
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* 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 java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.Fetch;
|
||||||
|
import org.hibernate.annotations.FetchMode;
|
||||||
|
import org.hibernate.annotations.NaturalId;
|
||||||
|
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Vlad Mihalcea
|
||||||
|
*/
|
||||||
|
public class FetchModeSelectTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
|
|
||||||
|
private static final Logger log = Logger.getLogger( FetchModeSelectTest.class );
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
|
return new Class<?>[] {
|
||||||
|
Department.class,
|
||||||
|
Employee.class,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||||
|
for ( long i = 0; i < 2; i++ ) {
|
||||||
|
Department department = new Department();
|
||||||
|
department.id = i + 1;
|
||||||
|
entityManager.persist( department );
|
||||||
|
|
||||||
|
for ( long j = 0; j < 3; j++ ) {
|
||||||
|
Employee employee1 = new Employee();
|
||||||
|
employee1.username = String.format( "user %d_%d", i, j );
|
||||||
|
employee1.department = department;
|
||||||
|
entityManager.persist( employee1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||||
|
//tag::fetching-strategies-fetch-mode-select-example[]
|
||||||
|
List<Department> departments = entityManager.createQuery(
|
||||||
|
"select d from Department d", Department.class )
|
||||||
|
.getResultList();
|
||||||
|
|
||||||
|
log.infof( "Fetched %d Departments", departments.size());
|
||||||
|
|
||||||
|
for (Department department : departments ) {
|
||||||
|
assertEquals( 3, department.getEmployees().size() );
|
||||||
|
}
|
||||||
|
//end::fetching-strategies-fetch-mode-select-example[]
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
//tag::fetching-strategies-fetch-mode-select-mapping-example[]
|
||||||
|
@Entity(name = "Department")
|
||||||
|
public static class Department {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "department", fetch = FetchType.LAZY)
|
||||||
|
@Fetch(FetchMode.SELECT)
|
||||||
|
private List<Employee> employees = new ArrayList<>();
|
||||||
|
|
||||||
|
//Getters and setters omitted for brevity
|
||||||
|
|
||||||
|
//end::fetching-strategies-fetch-mode-select-mapping-example[]
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Employee> getEmployees() {
|
||||||
|
return employees;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmployees(List<Employee> employees) {
|
||||||
|
this.employees = employees;
|
||||||
|
}
|
||||||
|
//tag::fetching-strategies-fetch-mode-select-mapping-example[]
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Employee")
|
||||||
|
public static class Employee {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@NaturalId
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
private Department department;
|
||||||
|
|
||||||
|
//Getters and setters omitted for brevity
|
||||||
|
|
||||||
|
}
|
||||||
|
//end::fetching-strategies-fetch-mode-select-mapping-example[]
|
||||||
|
}
|
@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* 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 java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.Fetch;
|
||||||
|
import org.hibernate.annotations.FetchMode;
|
||||||
|
import org.hibernate.annotations.NaturalId;
|
||||||
|
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Vlad Mihalcea
|
||||||
|
*/
|
||||||
|
public class FetchModeSubselectTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
|
|
||||||
|
private static final Logger log = Logger.getLogger( FetchModeSubselectTest.class );
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
|
return new Class<?>[] {
|
||||||
|
Department.class,
|
||||||
|
Employee.class,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||||
|
for ( long i = 0; i < 2; i++ ) {
|
||||||
|
Department department = new Department();
|
||||||
|
department.id = i + 1;
|
||||||
|
department.name = String.format( "Department %d", department.id );
|
||||||
|
entityManager.persist( department );
|
||||||
|
|
||||||
|
for ( long j = 0; j < 3; j++ ) {
|
||||||
|
Employee employee1 = new Employee();
|
||||||
|
employee1.username = String.format( "user %d_%d", i, j );
|
||||||
|
employee1.department = department;
|
||||||
|
entityManager.persist( employee1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||||
|
//tag::fetching-strategies-fetch-mode-subselect-example[]
|
||||||
|
List<Department> departments = entityManager.createQuery(
|
||||||
|
"select d " +
|
||||||
|
"from Department d " +
|
||||||
|
"where d.name like :token", Department.class )
|
||||||
|
.setParameter( "token", "Department%" )
|
||||||
|
.getResultList();
|
||||||
|
|
||||||
|
log.infof( "Fetched %d Departments", departments.size());
|
||||||
|
|
||||||
|
for (Department department : departments ) {
|
||||||
|
assertEquals( 3, department.getEmployees().size() );
|
||||||
|
}
|
||||||
|
//end::fetching-strategies-fetch-mode-subselect-example[]
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Department")
|
||||||
|
public static class Department {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
//tag::fetching-strategies-fetch-mode-subselect-mapping-example[]
|
||||||
|
@OneToMany(mappedBy = "department", fetch = FetchType.LAZY)
|
||||||
|
@Fetch(FetchMode.SUBSELECT)
|
||||||
|
private List<Employee> employees = new ArrayList<>();
|
||||||
|
//end::fetching-strategies-fetch-mode-subselect-mapping-example[]
|
||||||
|
|
||||||
|
//Getters and setters omitted for brevity
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Employee> getEmployees() {
|
||||||
|
return employees;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmployees(List<Employee> employees) {
|
||||||
|
this.employees = employees;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Employee")
|
||||||
|
public static class Employee {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@NaturalId
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
private Department department;
|
||||||
|
|
||||||
|
//Getters and setters omitted for brevity
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user