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.
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 🙂
Fixed it. Thanks!
Thank’s you so much! That’s helpful with me !
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
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?
Yes, you can reuse entity graphs that are defined on other entities. All graphs are globally defined for their persistence unit.
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 ?
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
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!
Awesome, happy to help 🙂
Very nice feature nicely explained. Thank you so much.
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
Hi Angad,
that’s correct. All to-many associations use FetchType.LAZY by default and all to-one use FetchType.EAGER.
I explained the FetchTypes in more detail in: https://thorben-janssen.com/entity-mappings-introduction-jpa-fetchtypes/
Regards,
Thorben
Thank you very much sir..!
Could also be done via FETCH JOIN begining with JPA 1.0
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
Wonderful. This gives me what I need about this lazy loading issues and coding with less mess.
Thank you =)
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 ?
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.
Really great and well written article. Solved some problems I had with eager loading. Thanks!
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
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
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
Hi Thorben,
thank you for this great article! I’m looking forward to try it out on my new project!
Best regards,
Valter Silva.
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!
Hi Mario,
no, you can’t do that with an EntityGraph. But you can do this with bytecode instrumentation as described here: https://docs.jboss.org/hibernate/orm/4.2/manual/en-US/html/ch20.html#performance-fetching-lazyproperties
Regards,
Thorben
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
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
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.
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
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.
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
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.
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.