I’ve been learning about JPA
and I found that we can use entity graph since JPA
2.1.
But I've not understood the merit of the entity graph yet.
I know that one of the merits to use an entity graph is we can specify only the data which we want to get among the whole entity, but in case we want to a whole entity, is there any other reason to use an entity graph?
Or we should use entity graphs only when we want to retrieve partial data?
If there is some other purpose or merit when we use entity graph, I would like to know it.
Entities in JPA are nothing but POJOs representing data that can be persisted to the database. An entity represents a table stored in a database. Every instance of an entity represents a row in the table.
Simply put, Entity Graphs are another way to describe a query in JPA 2.1. We can use them to formulate better-performing queries. In this tutorial, we're going to learn how to implement Entity Graphs with Spring Data JPA through a simple example.
What is JPA? Spring Boot JPA is a Java specification for managing relational data in Java applications. It allows us to access and persist data between Java object/ class and relational database. JPA follows Object-Relation Mapping (ORM).
Applying Named Entity Graph Annotations to Entity ClassesNamedEntityGraph 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 javax. persistence. NamedEntityGraphs class-level annotation.
The JPA Entity Graph
allows you to override the default fetch plan.
As I explained in this article, every entity has a default fetch plan that’s defined during entity mapping and instructs Hibernate how to fetch entity associations.
By default, @ManyToOne
and @OneToOne
associations use the FetchTyp.EAGER strategy, which is a terrible choice from a performance perspective. So, for this reason, it’s good practice to set all @ManyToOne
and @OneToOne
associations to use the FetchType.LAZY
strategy, like in the following example:
@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {
@Id
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
private Post post;
private String review;
//Getters and setters omitted for brevity
}
When fetching the PostComment
entity using the find
method:
PostComment comment = entityManager.find(PostComment.class, 1L);
Hibernate executes the following SQL query:
SELECT pc.id AS id1_1_0_,
pc.post_id AS post_id3_1_0_,
pc.review AS review2_1_0_
FROM post_comment pc
WHERE pc.id = 1
The post
association is fetched as a Proxy that has only the id
set by the post_id
Foreign Key column that was loaded by the aforementioned SQL query.
When accessing any non-id property of the post
Proxy:
LOGGER.info("The comment post title is '{}'", comment.getPost().getTitle());
A secondary SQL query is executed that fetched the Post
entity on-demand:
SELECT p.id AS id1_0_0_,
p.title AS title2_0_0_
FROM post p
WHERE p.id = 1
-- The comment post title is 'High-Performance Java Persistence, part 1'
If we want to override the default fetch plan and fetch the post
association eagerly at query execution time, we can use a JPQL query that instructs Hibernate to fetch the lazy association using the FETCH JOIN clause:
PostComment comment = entityManager.createQuery("""
select pc
from PostComment pc
left join fetch pc.post
where pc.id = :id
""", PostComment.class)
.setParameter("id", 1L)
.getSingleResult();
LOGGER.info("The comment post title is '{}'", comment.getPost().getTitle());
Then, the default fetch plan is going to be overridden, and the post
association will be fetched eagerly:
SELECT pc.id AS id1_1_0_,
p.id AS id1_0_1_,
pc.post_id AS post_id3_1_0_,
pc.review AS review2_1_0_,
p.title AS title2_0_1_
FROM post_comment pc
LEFT JOIN post p ON pc.post_id = p.id
WHERE pc.id = 1
The default fetch plan can also be overridden using a JPA Entity Graph. For instance, we could define a specific fetch plan using the following JPA @EntityGraph
annotation:
@Entity(name = "PostComment")
@Table(name = "post_comment")
@NamedEntityGraph(
name = "PostComment.post",
attributeNodes = @NamedAttributeNode("post")
)
public class PostComment {
//Code omitted for brevity
}
With the PostComment.post
Entity Graph in place, we can now load the PostComment
entity along with its associated post
entity, like this:
PostComment comment = entityManager.find(
PostComment.class,
1L,
Collections.singletonMap(
"javax.persistence.loadgraph",
entityManager.getEntityGraph("PostComment.post")
)
);
And, when executing the above find
method, Hibernate generates the following SQL SELECT query:
SELECT pc.id AS id1_1_0_,
pc.post_id AS post_id3_1_0_,
pc.review AS review2_1_0_,
p.id AS id1_0_1_,
p.title AS title2_0_1_
FROM post_comment pc
LEFT OUTER JOIN post p ON pc.post_id = p.id
WHERE pc.id = 1
If you're using Spring, then you can reference the JPA Entity Graph in a Repository method using the @EntityGraph
annotation:
@Repository
public interface PostCommentRepository
extends CrudRepository<PostComment, Long> {
@EntityGraph(
value = "PostComment.post",
type = EntityGraphType.LOAD
)
PostComment findById(Long id);
}
If you don't like annotations, then you can also build the JPA Entity Graph programmatically, using the createEntityGraph
method of the JPA EntityManager
, as illustrated by the following example:
EntityGraph<PostComment> postCommentGraph = entityManager
.createEntityGraph(PostComment.class);
postCommentGraph.addAttributeNodes("post");
PostComment comment = entityManager.find(
PostComment.class,
1L,
Collections.singletonMap(
"javax.persistence.loadgraph",
postCommentGraph
)
);
In JPA/Hibernate fetching entities with associations has always been a question for performance.
For code you can check my Github repository.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With