HHH-7758 - Write fetching Developer Guide chapter on fetching
This commit is contained in:
parent
d11165d4d2
commit
1176477e6a
|
@ -0,0 +1,228 @@
|
|||
<?xml version='1.0' encoding="UTF-8"?>
|
||||
|
||||
<chapter xml:id="fetching"
|
||||
xmlns="http://docbook.org/ns/docbook"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"
|
||||
xmlns:xl="http://www.w3.org/1999/xlink">
|
||||
|
||||
<title>Fetching</title>
|
||||
|
||||
<para>
|
||||
Fetching, essentially, is the process of grabbing data from the database and making it available to the
|
||||
application. Tuning how an application does fetching is one of the biggest factors in determining how an
|
||||
application will perform. Fetching too much data, in terms of width (values/columns) and/or
|
||||
depth (results/rows), adds unnecessary overhead in terms of both JDBC communication and ResultSet processing.
|
||||
Fetching too little data causes additional fetches to be needed. Tuning how an application
|
||||
fetches data presents a great opportunity to influence the application's overall performance.
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<title>The basics</title>
|
||||
|
||||
<para>
|
||||
The concept of fetching breaks down into two different questions.
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
When should the data be fetched? Now? Later?
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
How should the data be fetched?
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<note>
|
||||
<para>
|
||||
"now" is generally termed <phrase>eager</phrase> or <phrase>immediate</phrase>. "later" is
|
||||
generally termed <phrase>lazy</phrase> or <phrase>delayed</phrase>.
|
||||
</para>
|
||||
</note>
|
||||
|
||||
<para>
|
||||
There are a number of scopes for defining fetching:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
<emphasis>static</emphasis> - Static definition of fetching strategies is done in the
|
||||
mappings. The statically-defined fetch strategies is used in the absence of any dynamically
|
||||
defined strategies <footnote><para>Except in the case of HQL/JPQL; see xyz</para></footnote>.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<emphasis>dynamic</emphasis> (sometimes referred to as runtime) - Dynamic definition is
|
||||
really use-case centric. There are 2 main ways to define dynamic fetching:
|
||||
</para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
<emphasis>fetch profiles</emphasis> - defined in mappings, but can be
|
||||
enabled/disabled on the Session.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
HQL/JPQL and both Hibernate and JPA Criteria queries have the ability to specify
|
||||
fetching, specific to said query.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<variablelist>
|
||||
<title>The strategies</title>
|
||||
<varlistentry>
|
||||
<term>SELECT</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Performs a separate SQL select to load the data. This can either be EAGER (the second select
|
||||
is issued immediately) or LAZY (the second select is delayed until the data is needed). This
|
||||
is the strategy generally termed <phrase>N+1</phrase>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>JOIN</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Inherently an EAGER style of fetching. The data to be fetched is obtained through the use of
|
||||
an SQL join.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>BATCH</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Performs a separate SQL select to load a number of related data items using an
|
||||
IN-restriction as part of the SQL WHERE-clause based on a batch size. Again, this can either
|
||||
be EAGER (the second select is issued immediately) or LAZY (the second select is delayed until
|
||||
the data is needed).
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>SUBSELECT</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Performs a separate SQL select to load associated data based on the SQL restriction used to
|
||||
load the owner. Again, this can either be EAGER (the second select is issued immediately)
|
||||
or LAZY (the second select is delayed until the data is needed).
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Applying fetch strategies</title>
|
||||
|
||||
<para>
|
||||
Let's consider these topics as it relates to an simple domain model and a few use cases.
|
||||
</para>
|
||||
|
||||
<example>
|
||||
<title>Sample domain model</title>
|
||||
<programlisting role="JAVA"><xi:include href="extras/Employee.java" parse="text"/></programlisting>
|
||||
<programlisting role="JAVA"><xi:include href="extras/Department.java" parse="text"/></programlisting>
|
||||
<programlisting role="JAVA"><xi:include href="extras/Project.java" parse="text"/></programlisting>
|
||||
</example>
|
||||
|
||||
<important>
|
||||
<para>
|
||||
The Hibernate recommendation is to statically mark all associations lazy and to use dynamic fetching
|
||||
strategies for eagerness. This is unfortunately at odds with the JPA specification which defines that
|
||||
all one-to-one and many-to-one associations should be eagerly fetched by default. Hibernate, as a JPA
|
||||
provider honors that default.
|
||||
</para>
|
||||
</important>
|
||||
|
||||
<section>
|
||||
<title>No fetching</title>
|
||||
<subtitle>The login use-case</subtitle>
|
||||
<para>
|
||||
For the first use case, consider the application's login process for an Employee. Lets assume that
|
||||
login only requires access to the Employee information, not Project nor Department information.
|
||||
</para>
|
||||
|
||||
<example>
|
||||
<title>No fetching example</title>
|
||||
<programlisting role="JAVA"><xi:include href="extras/Login.java" parse="text"/></programlisting>
|
||||
</example>
|
||||
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
|
||||
<example>
|
||||
<title>No fetching (scalar) example</title>
|
||||
<programlisting role="JAVA"><xi:include href="extras/LoginScalar.java" parse="text"/></programlisting>
|
||||
</example>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Dynamic fetching via queries</title>
|
||||
<subtitle>The projects for an employee use-case</subtitle>
|
||||
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
|
||||
<example>
|
||||
<title>Dynamic query fetching example</title>
|
||||
<programlisting role="JAVA"><xi:include href="extras/ProjectsForAnEmployeeHql.java" parse="text"/></programlisting>
|
||||
<programlisting role="JAVA"><xi:include href="extras/ProjectsForAnEmployeeCriteria.java" parse="text"/></programlisting>
|
||||
</example>
|
||||
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Dynamic fetching via profiles</title>
|
||||
<subtitle>The projects for an employee use-case using natural-id</subtitle>
|
||||
|
||||
<para>
|
||||
Suppose we wanted to leverage loading by natural-id to obtain the Employee information in the
|
||||
"projects for and employee" use-case. Loading by natural-id uses the statically defined fetching
|
||||
strategies, but does not expose a means to define load-specific fetching. So we would leverage a
|
||||
fetch profile.
|
||||
</para>
|
||||
|
||||
<example>
|
||||
<title>Fetch profile example</title>
|
||||
<programlisting role="JAVA"><xi:include href="extras/FetchOverrides.java" parse="text"/></programlisting>
|
||||
<programlisting role="JAVA"><xi:include href="extras/ProjectsForAnEmployeeFetchProfile.java" parse="text"/></programlisting>
|
||||
</example>
|
||||
|
||||
<para>
|
||||
Here the Employee is obtained by natural-id lookup and the Employee's Project data is fetched eagerly.
|
||||
If the Employee data is resolved from cache, the Project data is resolved on its own. However,
|
||||
if the Employee data is not resolved in cache, the Employee and Project data is resolved in one
|
||||
SQL query via join as we saw above.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<!-- todo : document special fetching considerations such as batch fetching, subselect fetching and extra laziness -->
|
||||
|
||||
|
||||
</chapter>
|
|
@ -0,0 +1,10 @@
|
|||
@Entity
|
||||
public class Department {
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@OneToMany(mappedBy="department")
|
||||
private List<Employees> employees;
|
||||
|
||||
...
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
@Entity
|
||||
public class Employee {
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@NaturalId
|
||||
private String userid;
|
||||
|
||||
@Column( name="pswd" )
|
||||
@ColumnTransformer( read="decrypt(pswd)" write="encrypt(?)" )
|
||||
private String password;
|
||||
|
||||
private int accessLevel;
|
||||
|
||||
@ManyToOne( fetch=LAZY )
|
||||
@JoinColumn
|
||||
private Department department;
|
||||
|
||||
@ManyToMany(mappedBy="employees")
|
||||
@JoinColumn
|
||||
private Set<Project> projects;
|
||||
|
||||
...
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
@FetchProfile(
|
||||
name="employee.projects",
|
||||
fetchOverrides={
|
||||
@FetchOverride(
|
||||
entity=Employee.class,
|
||||
association="projects",
|
||||
mode=JOIN
|
||||
)
|
||||
}
|
||||
)
|
|
@ -0,0 +1,4 @@
|
|||
String loginHql = "select e from Employee e where e.userid = :userid and e.password = :password";
|
||||
Employee employee = (Employee) session.createQuery( loginHql )
|
||||
...
|
||||
.uniqueResult();
|
|
@ -0,0 +1,4 @@
|
|||
String loginHql = "select e.accessLevel from Employee e where e.userid = :userid and e.password = :password";
|
||||
Employee employee = (Employee) session.createQuery( loginHql )
|
||||
...
|
||||
.uniqueResult();
|
|
@ -0,0 +1,10 @@
|
|||
@Entity
|
||||
public class Project {
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@ManyToMany
|
||||
private Set<Employee> employees;
|
||||
|
||||
...
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
String userid = ...;
|
||||
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Employee> criteria = cb.createQuery( Employee.class );
|
||||
Root<Employee> root = criteria.from( Employee.class );
|
||||
root.fetch( Employee_.projects );
|
||||
criteria.select( root );
|
||||
criteria.where(
|
||||
cb.equal( root.get( Employee_.userid ), cb.literal( userid ) )
|
||||
);
|
||||
Employee e = entityManager.createQuery( criteria ).getSingleResult();
|
|
@ -0,0 +1,4 @@
|
|||
String userid = ...;
|
||||
session.enableFetchProfile( "employee.projects" );
|
||||
Employee e = (Employee) session.bySimpleNaturalId( Employee.class )
|
||||
.load( userid );
|
|
@ -0,0 +1,5 @@
|
|||
String userid = ...;
|
||||
String hql = "select e from Employee e join fetch e.projects where e.userid = :userid";
|
||||
Employee e = (Employee) session.createQuery( hql )
|
||||
.setParameter( "userid", userid )
|
||||
.uniqueResult();
|
Loading…
Reference in New Issue