What's the goal of the JPA Entity Graph?
Asked Answered
H

2

6

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.

Helpful answered 29/7, 2015 at 14:33 Comment(2)
This helps? radcortez.com/jpa-entity-graphsMcginn
thank you for the Link, it helpled a lot!Helpful
F
3

In JPA/Hibernate fetching entities with associations has always been a question for performance.

  • Lazily loading associations with in a transaction again and again results in n+1 select issues and to avoid such issues JPQL join fetch and Criteria api joins are used. But fetching data with these two also result in Cross join issues means Cross join of all table records are returned to application by hibernate.
  • Also altering fetch variable defined in annotations on entity level is also not a good option for use case to use case basis.
  • Hence to solve the above two issues entity graphs has been introduced. All nodes defined in entity graphs are always eager fetched irrespective of their definition on entity level. These graphs are passed as a hint to queries.
  • By passing graphs as a hint Cross join issues are solved as well annotation level specified fetch behavior of an association can also be changed.

For code you can check my Github repository.

Forepeak answered 18/1, 2019 at 16:57 Comment(0)
M
9

The JPA Entity Graph allows you to override the default fetch plan.

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'

Overriding the default Fetch Plan

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

Declarative JPA Entity Graph

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);
}

Programmatic JPA Entity Graph

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
    )
);
Mawkish answered 8/8, 2021 at 6:17 Comment(0)
F
3

In JPA/Hibernate fetching entities with associations has always been a question for performance.

  • Lazily loading associations with in a transaction again and again results in n+1 select issues and to avoid such issues JPQL join fetch and Criteria api joins are used. But fetching data with these two also result in Cross join issues means Cross join of all table records are returned to application by hibernate.
  • Also altering fetch variable defined in annotations on entity level is also not a good option for use case to use case basis.
  • Hence to solve the above two issues entity graphs has been introduced. All nodes defined in entity graphs are always eager fetched irrespective of their definition on entity level. These graphs are passed as a hint to queries.
  • By passing graphs as a hint Cross join issues are solved as well annotation level specified fetch behavior of an association can also be changed.

For code you can check my Github repository.

Forepeak answered 18/1, 2019 at 16:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.