Creating Fetch Plans with Entity Graphs
We are working on a fresh, updated Jakarta EE Tutorial. This section hasn’t yet been updated. |
This chapter explains how to use entity graphs to create fetch plans for Jakarta Persistence operations and queries.
Overview of Using Fetch Plans and Entity Graphs
Entity graphs are templates for a particular Persistence query or operation. They are used when creating fetch plans, or groups of persistent fields that are retrieved at the same time. Application developers use fetch plans to group together related persistent fields to improve runtime performance.
By default, entity fields or properties are fetched lazily. Developers specify fields or properties as part of a fetch plan, and the persistence provider will fetch them eagerly.
For example, an email application that stores messages as EmailMessage
entities prioritizes fetching some fields over others.
The sender, subject, and date will be viewed the most often, in mailbox views and when the message is displayed.
The EmailMessage
entity has a collection of related EmailAttachment
entities.
For performance reasons the attachments should not be fetched until they are needed, but the file names of the attachment are important.
A developer working on this application might make a fetch plan that eagerly fetches the important fields from EmailMessage
and EmailAttachment
while fetching the lower priority data lazily.
Entity Graph Basics
You can create entity graphs statically by using annotations or a deployment descriptor, or dynamically by using standard interfaces.
You can use an entity graph with the EntityManager.find
method or as part of a JPQL or Criteria API query by specifying the entity graph as a hint to the operation or query.
Entity graphs have attributes that correspond to the fields that will be eagerly fetched during a find
or query operation.
The primary key and version fields of the entity class are always fetched and do not need to be explicitly added to an entity graph.
The Default Entity Graph
By default, all fields in an entity are fetched lazily unless the fetch
attribute of the entity metadata is set to jakarta.persistence.FetchType.EAGER
.
The default entity graph consists of all the fields of an entity whose fields are set to be eagerly fetched.
For example, the following EmailMessage
entity specifies that some fields will be fetched eagerly:
@Entity
public class EmailMessage implements Serializable {
@Id
String messageId;
@Basic(fetch=EAGER)
String subject;
String body;
@Basic(fetch=EAGER)
String sender;
@OneToMany(mappedBy="message", fetch=LAZY)
Set<EmailAttachment> attachments;
...
}
The default entity graph for this entity would contain the messageId
, subject
, and sender
fields, but not the body
or attachments
fields.
Using Entity Graphs in Persistence Operations
Entity graphs are used by creating an instance of the jakarta.persistence.EntityGraph
interface by calling either EntityManager.getEntityGraph
for named entity graphs or EntityManager.createEntityGraph
for creating dynamic entity graphs.
A named entity graph is an entity graph specified by the @NamedEntityGraph
annotation applied to entity classes, or the named-entity-graph
element in the application’s deployment descriptors.
Named entity graphs defined within the deployment descriptor override any annotation-based entity graphs with the same name.
The created entity graph can be either a fetch graph or a load graph.
Fetch Graphs
To specify a fetch graph, set the jakarta.persistence.fetchgraph
property when you execute an EntityManager.find
or query operation.
A fetch graph consists of only the fields explicitly specified in the EntityGraph
instance, and ignores the default entity graph settings.
In the following example, the default entity graph is ignored, and only the body
field is included in the dynamically created fetch graph:
EntityGraph<EmailMessage> eg = em.createEntityGraph(EmailMessage.class);
eg.addAttributeNodes("body");
...
Properties props = new Properties();
props.put("jakarta.persistence.fetchgraph", eg);
EmailMessage message = em.find(EmailMessage.class, id, props);
Load Graphs
To specify a load graph, set the jakarta.persistence.loadgraph
property when you execute an EntityManager.find
or query operation.
A load graph consists of the fields explicitly specified in the EntityGraph
instance plus any fields in the default entity graph.
In the following example, the dynamically created load graph contains all the fields in the default entity graph plus the body
field:
EntityGraph<EmailMessage> eg = em.createEntityGraph(EmailMessage.class);
eg.addAttributeNodes("body");
...
Properties props = new Properties();
props.put("jakarta.persistence.loadgraph", eg);
EmailMessage message = em.find(EmailMessage.class, id, props);
Using Named Entity Graphs
Named entity graphs are created using annotations applied to entity classes or the named-entity-graph
element and its sub-elements in the application’s deployment descriptor.
The persistence provider will scan for all named entity graphs, defined in both annotations and in XML, within an application.
A named entity graph set using an annotation may be overridden using named-entity-graph
.
Applying Named Entity Graph Annotations to Entity Classes
The jakarta.persistence.NamedEntityGraph
annotation defines a single named entity graph and is applied at the class level.
Multiple @NamedEntityGraph
annotations may be defined for a class by adding them within a jakarta.persistence.NamedEntityGraphs
class-level annotation.
The @NamedEntityGraph
annotation must be applied on the root of the graph of entities.
That is, if the EntityManager.find
or query operation has as its root entity the EmailMessage
class, the named entity graph used in the operation must be defined in the EmailMessage
class:
@NamedEntityGraph
@Entity
public class EmailMessage {
@Id
String messageId;
String subject;
String body;
String sender;
}
In this example, the EmailMessage
class has a @NamedEntityGraph
annotation to define a named entity graph that defaults to the name of the class, EmailMessage
.
No fields are included in the @NamedEntityGraph
annotation as attribute nodes, and the fields are not annotated with metadata to set the fetch type, so the only field that will be eagerly fetched in either a load graph or fetch graph is messageId
.
The attributes of a named entity graph are the fields of the entity that should be included in the entity graph.
Add the fields to the entity graph by specifying them in the attributeNodes
element of @NamedEntityGraph
with a jakarta.persistence.NamedAttributeNode
annotation:
@NamedEntityGraph(name="emailEntityGraph", attributeNodes={
@NamedAttributeNode("subject"),
@NamedAttributeNode("sender")
})
@Entity
public class EmailMessage { ... }
In this example, the name of the named entity graph is emailEntityGraph
and includes the subject
and sender
fields.
Multiple @NamedEntityGraph
definitions may be applied to a class by grouping them within a @NamedEntityGraphs
annotation.
In the following example, two entity graphs are defined on the EmailMessage
class.
One is for a preview pane, which fetches only the sender, subject, and body of the message.
The other is for a full view of the message, including any message attachments:
@NamedEntityGraphs({
@NamedEntityGraph(name="previewEmailEntityGraph", attributeNodes={
@NamedAttributeNode("subject"),
@NamedAttributeNode("sender"),
@NamedAttributeNode("body")
}),
@NamedEntityGraph(name="fullEmailEntityGraph", attributeNodes={
@NamedAttributeNode("sender"),
@NamedAttributeNode("subject"),
@NamedAttributeNode("body"),
@NamedAttributeNode("attachments")
})
})
@Entity
public class EmailMessage { ... }
Using Entity Graphs in Query Operations
To specify entity graphs for both typed and untyped queries, call the setHint
method on the query object and specify either jakarta.persistence.loadgraph
or jakarta.persistence.fetchgraph
as the property name and an EntityGraph
instance as the value:
EntityGraph<EmailMessage> eg = em.getEntityGraph("previewEmailEntityGraph");
List<EmailMessage> messages = em.createNamedQuery("findAllEmailMessages")
.setParameter("mailbox", "inbox")
.setHint("jakarta.persistence.loadgraph", eg)
.getResultList();
In this example, the previewEmailEntityGraph
is used for the findAllEmailMessages
named query.
Typed queries use the same technique:
EntityGraph<EmailMessage> eg = em.getEntityGraph("previewEmailEntityGraph");
CriteriaQuery<EmailMessage> cq = cb.createQuery(EmailMessage.class);
Root<EmailMessage> message = cq.from(EmailMessage.class);
TypedQuery<EmailMessage> q = em.createQuery(cq);
q.setHint("jakarta.persistence.loadgraph", eg);
List<EmailMessage> messages = q.getResultList();