| |

JPA Entity Graphs: How to Define and Use a @NamedEntityGraph


Take your skills to the next level!

The Persistence Hub is the place to be for every Java developer. It gives you access to all my premium video courses, monthly Java Persistence News, monthly coding problems, and regular expert sessions.


Lazy loading is often an issue with JPA. You have to define at the entity if you want to use FetchType.LAZY (default) or FetchType.EAGER to load the relation and this mode is always used. FetchType.EAGER is only used if we want to always load the relation. FetchType.LAZY is used in almost all of the cases to get a well performing and scalable application.

But this is not without drawbacks. If you have to use an element of the relation, you need to make sure, that the relation gets initialized within the transaction that load the entity from the database. This can be done by using a specific query that reads the entity and the required relations from the database. But this will result in use case specific queries. Another option is to access the relation within your business code which will result in an additional query for each relation. Both approaches are far from perfect.

Entity graphs are a better solution for it. The definition of an entity graph is independent of the query and defines which attributes to fetch from the database. An entity graph can be used as a fetch or a load graph. If a fetch graph is used, only the attributes specified by the entity graph will be treated as FetchType.EAGER. All other attributes will be lazy. If a load graph is used, all attributes that are not specified by the entity graph will keep their default fetch type.

Let’s have a look how to define and use an entity graph.

The example entities

For this example, we will use an order with a list of items and each item has a product. All associations between are fetched lazily.

The Order entity:

@Entity
@Table(name = "purchase_order")
public class Order {
 
   @Id
   @GeneratedValue
   private Long id;
 
   @Version
   private int version;
 
   private String orderNumber;
 
   @OneToMany(mappedBy = "order", fetch = FetchType.LAZY)
   private Set<OrderItem> items = new HashSet<OrderItem>();
 
   ...
}

The OrderItem entity:

@Entity
public class OrderItem {
 
   @Id
   @GeneratedValue
   private Long id;
 
   @Version
   private int version;
 
   private int quantity;
 
   @ManyToOne
   private Order order;
 
   @ManyToOne(fetch = FetchType.LAZY)
   private Product product;
 
   ...
}

The Product entity:

@Entity
public class Product {
 
   @Id
   @GeneratedValue
   private Long id;
 
   @Version
   private int version;
 
   private String name;
 
   ...
}

Named entity graph

The definition of a named entity graph is done by the @NamedEntityGraph annotation at the entity. It defines a unique name and a list of attributes (the attributeNodes) that shall be loaded.
The following example shows the definition of the entity graph graph.Order.items which will load the list of OrderItem of an Order.

@Entity
@Table(name = "purchase_order")
@NamedEntityGraph(name = "graph.Order.items", 
                  attributeNodes = @NamedAttributeNode("items"))
public class Order { ... }

Now that we have defined the entity graph, we can use it in a query. Therefore we need to create a Map with query hints and set it as an additional parameter on a find or query method call.
The following code snippet shows how to use a named entity graph as a fetch graph in a find statement.

EntityGraph graph = this.em.getEntityGraph("graph.Order.items");
 
Map hints = new HashMap();
hints.put("jakarta.persistence.fetchgraph", graph);
 
return this.em.find(Order.class, orderId, hints);

Named sub graph

We used the entity graph to define the fetch operation of the Order entity. If we want to do the same for the OrderItem entity, we can do this with an entity sub graph. The definition of a named sub graph is similar to the definition of an named entity graph and can be referenced as an attributeNode.
The following code snippets shows the definition of a sub graph to load the Product of each OrderItem. The defined entity graph will fetch an Order with all OrderItems and their Products.

@Entity
@Table(name = "purchase_order")
@NamedEntityGraph(name = "graph.Order.items", 
                  attributeNodes = @NamedAttributeNode(value = "items", subgraph = "items"), 
                  subgraphs = @NamedSubgraph(name = "items", attributeNodes = @NamedAttributeNode("product")))
public class Order { ... }

What’s happening inside?

OK, from a development point of view entity graphs are great. They are easy to use and we do not need to write additional code to avoid lazy loading issues. But what is happening inside? How many queries are send to the database? Lets have a look at the hibernate debug log.

21:56:08,285 DEBUG [org.hibernate.loader.plan.build.spi.LoadPlanTreePrinter] (pool-2-thread-1) LoadPlan(entity=blog.thoughts.on.java.jpa21.entity.graph.model.Order) - Returns - EntityReturnImpl(entity=blog.thoughts.on.java.jpa21.entity.graph.model.Order, querySpaceUid=<gen:0>, path=blog.thoughts.on.java.jpa21.entity.graph.model.Order) - CollectionAttributeFetchImpl(collection=blog.thoughts.on.java.jpa21.entity.graph.model.Order.items, querySpaceUid=<gen:1>, path=blog.thoughts.on.java.jpa21.entity.graph.model.Order.items) - (collection element) CollectionFetchableElementEntityGraph(entity=blog.thoughts.on.java.jpa21.entity.graph.model.OrderItem, querySpaceUid=<gen:2>, path=blog.thoughts.on.java.jpa21.entity.graph.model.Order.items.<elements>) - EntityAttributeFetchImpl(entity=blog.thoughts.on.java.jpa21.entity.graph.model.Product, querySpaceUid=<gen:3>, path=blog.thoughts.on.java.jpa21.entity.graph.model.Order.items.<elements>.product) - QuerySpaces - EntityQuerySpaceImpl(uid=<gen:0>, entity=blog.thoughts.on.java.jpa21.entity.graph.model.Order) - SQL table alias mapping - order0_ - alias suffix - 0_ - suffixed key columns - {id1_2_0_} - JOIN (JoinDefinedByMetadata(items)) : <gen:0> -> <gen:1> - CollectionQuerySpaceImpl(uid=<gen:1>, collection=blog.thoughts.on.java.jpa21.entity.graph.model.Order.items) - SQL table alias mapping - items1_ - alias suffix - 1_ - suffixed key columns - {order_id4_2_1_} - entity-element alias suffix - 2_ - 2_entity-element suffixed key columns - id1_0_2_ - JOIN (JoinDefinedByMetadata(elements)) : <gen:1> -> <gen:2> - EntityQuerySpaceImpl(uid=<gen:2>, entity=blog.thoughts.on.java.jpa21.entity.graph.model.OrderItem) - SQL table alias mapping - items1_ - alias suffix - 2_ - suffixed key columns - {id1_0_2_} - JOIN (JoinDefinedByMetadata(product)) : <gen:2> -> <gen:3> - EntityQuerySpaceImpl(uid=<gen:3>, entity=blog.thoughts.on.java.jpa21.entity.graph.model.Product) - SQL table alias mapping - product2_ - alias suffix - 3_ - suffixed key columns - {id1_1_3_} 
 
21:56:08,285 DEBUG [org.hibernate.loader.entity.plan.EntityLoader] (pool-2-thread-1) Static select for entity blog.thoughts.on.java.jpa21.entity.graph.model.Order [NONE:-1]: select order0_.id as id1_2_0_, order0_.orderNumber as orderNum2_2_0_, order0_.version as version3_2_0_, items1_.order_id as order_id4_2_1_, items1_.id as id1_0_1_, items1_.id as id1_0_2_, items1_.order_id as order_id4_0_2_, items1_.product_id as product_5_0_2_, items1_.quantity as quantity2_0_2_, items1_.version as version3_0_2_, product2_.id as id1_1_3_, product2_.name as name2_1_3_, product2_.version as version3_1_3_ from purchase_order order0_ left outer join OrderItem items1_ on order0_.id=items1_.order_id left outer join Product product2_ on items1_.product_id=product2_.id where order0_.id=?

The log shows that only one query is created. Hibernate uses the entity graph to create a load plan with all 3 entities (Order, OrderItem and Product) and load them with one query.

Conclusion

We defined an entity graph that tells the entity manager to fetch a graph of 3 related entities from the database (Order, OrderItem and Product). The definition and usage of the entity graph is query independent and results in only one select statement. So the main drawbacks of the JPA 2.0 approaches (mentioned in the beginning) are solved.

34 Comments

  1. Avatar photo Jordan Nickolov says:

    A nice article.
    There is a small typo: the table name in the first code segment is “purchaseOrder”, but it is “purchase_order” in the following segments 🙂

  2. Avatar photo Vo Duy Khanh says:

    Thank’s you so much! That’s helpful with me !

  3. Avatar photo Thorben Janssen says:

    Hi Anis,

    All graph definitions are available within their persistence unit. So, yes! You can reference a graph that’s defined on a different entity.

    Regards,
    Thorben

  4. Is it possible for “subgraph” to refer to another NamedEntityGraph, instead of defining it locally? Let’s say if same “subgraph” is required in multiple entities then have to duplicate it everywhere. Can we define one namedEntityGraph and use it as “subGraph” inother places?

    1. Avatar photo Thorben Janssen says:

      Yes, you can reuse entity graphs that are defined on other entities. All graphs are globally defined for their persistence unit.

  5. Hello,
    Thanks for the great article and great explanation of the problem.
    I see that this feature of JPA 2.1 is great for solving the N+1 Problem. However, my doubts are that, in a real world application that would probably scale and have at least dozens of thousands rows per table, using Entity Graphs can be really painful for the Database, since the numerous joins.
    What are your thoughts on this ?

    1. Avatar photo Thorben Janssen says:

      Hi,
      That depends on your table model and the conditions in your WHERE clause. In some situations, joining multiple associations creates huge result sets which create new problems.
      As so often, you need to select the best option for your specific use case.
      Regards,
      Thorben

  6. i didnt understood entity grapgs from hibernate reference doc but from your article i get it all down to my brain. explained in a very simpler way.
    Thanks a lot!

    1. Avatar photo Thorben Janssen says:

      Awesome, happy to help 🙂

  7. Avatar photo Jyoti Prakash Rai says:

    Very nice feature nicely explained. Thank you so much.

  8. Avatar photo Angad Bansode says:

    I have one big question as my knowledge @AnyToMany default fetch type is Lazy and @ManyToAny default fetch type is Eager (i.e @OneToMany –default fetchType = Lazy , @ManyToOne –default FetchType = Eager) . Is it correct info.please reply me as soon as possible

      1. Avatar photo Angad Bansode says:

        Thank you very much sir..!

  9. Avatar photo Thomas Götzinger says:

    Could also be done via FETCH JOIN begining with JPA 1.0

    1. Avatar photo Thorben Janssen says:

      Hi Thomas,

      that’s right. EntityGraphs provide the advantage that they’re independent of the query. So, you can use the same query with or without the FetchGraph.
      If that’s a real advantage depends on your query and use cases, of course. 😉

      Regards,
      Thorben

  10. Wonderful. This gives me what I need about this lazy loading issues and coding with less mess.
    Thank you =)

  11. Is it possible to use NamedEntityGraph without the primary key/id field ? In the above example, say I have an order number which is unique but still not the id field . Will I be able to load an entity graph based on the order number ?

    1. Avatar photo Thorben Janssen says:

      I don’t understand your question. Are you asking about the query with which you want to use the graph?
      You can use entity graphs with all JPQL and Criteria queries. You don’t have to select an entity by its primary key.

  12. Really great and well written article. Solved some problems I had with eager loading. Thanks!

  13. Avatar photo Alexander Kirilov says:

    Hello, thank you for the great article ! This feature gives a real solution to the age old N+1 problem.
    I wonder however, if it is possible to do sub-subgraph in order to load even more entities ?

    Ex: Order -> OrderItem -> Product -> ProductLine ?

    Thank you!
    Alexander

    1. Avatar photo Thorben Janssen says:

      Hi Alexander,

      yes, subgraphs can also use subgraphs so that you can define a huge tree of entities that get loaded from the database.
      But make sure to load only the relationships you really need and keep in mind that the unnecessary loading of attributes also creates an overhead. POJO projections are most often the better approach if you need a few attributes out of huge tree of entities.

      Regards,
      Thorben

      1. Avatar photo Bipin Shrestha says:

        Hi Thorben,
        Great Article. Thank you.
        Can you show with example how to use subgraphs inside subgraphs?

        Let us consider this hierarchy:
        Order -> OrderItem -> Product -> ProductLine

  14. Avatar photo Valter Silva says:

    Hi Thorben,
    thank you for this great article! I’m looking forward to try it out on my new project!

    Best regards,
    Valter Silva.

  15. Avatar photo MARIO BALABAN says:

    Hello, is it possible to use this feature for changing the default Eager loading of a single attribute ? For example if I have an Entity that has a JsonAttribute (custom user type) that is mapped to a varchar or json column in the DB and in some cases I need to override the Eager loading and let hibernate Lazy load this property ? Thanks!

  16. Avatar photo Ardhendu Shekhar Singh says:

    I just read this article and looking forward to implement in one of the upcoming functionality in my project and see how it works.. But by going through this article I feel that this should be helpful. Will revert back if I am done with implementation successfully.
    Cheers

    1. Avatar photo Thorben Janssen says:

      Hi Ardhendu,

      thanks for your comment. From my point of view, EntityGraphs are one of the most useful features added in JPA 2.1.

      Regards,
      Thorben

  17. Thanks for posting this, very informative.

    IMHO, this feature is equivalent to writing NamedQueries, just with more awkward syntax. You can specify the join fetches in your NamedQuery to ensure only one statement is sent to the DB. You mention that the query approach results in ‘use case specific queries’ but I don’t see how the EntityGraph is anything more than a ‘use case specific graph’ following that rationale.

    1. Avatar photo Thorben Janssen says:

      Hi Robert,

      yes, the graph is almost always specific to the use case. But you can reuse the query in different use cases that require different fetching.

      Regards,
      Thorben

  18. Nice article. Thanks.

    I have a question: What are the benefits of use Entity Graph instead of fetch join approach? If I am right, If we need to load different relations we need to define different entity graphs, the same disadvantage that in the fetch join approach.

    1. Avatar photo Thorben Janssen says:

      Hi Mario,

      in my experience, the query is most often more complicated than the EntityGraph. So, it can be useful to reuse the query.
      If that’s not the case or the query is specific to the use case, you can also use a JOIN FETCH clause.

      Regards,
      Thorben

  19. Avatar photo Mark Spritzler says:

    Wow, I am surprised no one has commented on this article. This is one of the coolest features I have seen in JPA, it does solve a huge problem that many JPA developers have. In many cases developers haven’t even been implementing the Use Case @NamedQueries approach (Which I believed was a HUGE MUST DO before) I have seen way too many applications crumble under performance because they took a lazy approach and N+1 or eager fetch everything.

    1. Avatar photo Thorben Janssen says:

      Thanks for your comment Mark!

      I also think this is one of the best features in JPA 2.1. My other favorite is the Attribute Converter.

Comments are closed.