JPA Entity Graphs: How to Dynamically Define and Use an EntityGraph
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.
This is my second post on Entity Graphs. The first post described the usage of named entity graphs. These can be used to define a graph of entities and/or attributes at compile time that shall be fetched with a find or query method. Dynamic entity graphs do to the same but in a dynamic way. This means you can use the EntityGraph API to define your entity graph at runtime.
If you have missed the first post and want to read how to define a named entity graph or how to solve lazy loading without it, check this post: Entity Graph – Part 1: Named entity graphs
The example entities
We will use the same example as in the previous post. So you can skip this paragraph if you have read the other one.
We will use 3 entities. These are Order, OrderItem and Product. An Order might include multiple OrderItems and each OrderItem belongs to one Product. The FetchType of all these relations it FetchType.LAZY. So the entity manager will not fetch them from the database by default and initialize them with a proxy instead.
The Order entity:
@Entity @Table(name = "purchaseOrder") @NamedEntityGraph(name = "graph.Order.items", attributeNodes = @NamedAttributeNode(value = "items", subgraph = "items"), subgraphs = @NamedSubgraph(name = "items", attributeNodes = @NamedAttributeNode("product"))) public class Order implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", updatable = false, nullable = false) private Long id = null; @Version @Column(name = "version") private int version = 0; @Column private String orderNumber; @OneToMany(mappedBy = "order", fetch = FetchType.LAZY) private Set<OrderItem> items = new HashSet<OrderItem>(); ...
The OrderItem entity:
@Entity public class OrderItem implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", updatable = false, nullable = false) private Long id = null; @Version @Column(name = "version") private int version = 0; @Column private int quantity; @ManyToOne private Order order; @ManyToOne(fetch = FetchType.LAZY) private Product product;
The Product entity:
@Entity public class Product implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", updatable = false, nullable = false) private Long id = null; @Version @Column(name = "version") private int version = 0; @Column private String name;
Dynamic entity graph
So lets define a dynamic entity graph. We will do the same as in the first post and define a simple entity graph that tells the entity manager to fetch an Order with all associated OrderItems. Therefore we use the createEntityGraph(Class rootType) method of the entity manager to create an entity graph for the Order entity. In the next step, we create a list of all attributes of the Order entity that shall be fetched with this entity graph. We only need to add the attribute items, because we will use this entity graph as a loadgraph and all other attributes are eager by default.
If we would use this entity graph as a fetchgraph, we would need to add all attributes to the list that should be fetched from the database.
EntityGraph<Order> graph = this.em.createEntityGraph(Order.class); graph.addAttributeNodes("items"); Map<String, Object> hints = new HashMap<String, Object>(); hints.put("javax.persistence.loadgraph", graph); this.em.find(Order.class, orderId, hints);
OK, dynamically defining which attributes of an entity shall be fetched from the database is nice. But what if we need a graph of entities? Like fetching an Order with all its OrderItems and their Product?
This can be done with a sub graph. A sub graph is basically an entity graph that is embedded into another entity graph or entity sub graph. The definition of a sub graph is similar to the definition of an entity graph. To create and embed the sub graph into an entity graph, we need to call the addSubgraph(String attributeName) method on an EntityGraph object. This will create a sub graph for the attribute with the given name. In the next step, we need to define the list of attributes that shall be fetched with this sub graph.
The following snippet shows the definition of an entity graph with an entity sub graph which tell the entity manager to fetch an Order with its OrderItems and their Product.
EntityGraph<Order> graph = this.em.createEntityGraph(Order.class); Subgraph<OrderItem> itemGraph = graph.addSubgraph("items"); itemGraph.addAttributeNodes("product"); Map<String, Object> hints = new HashMap<String, Object>(); hints.put("javax.persistence.loadgraph", graph); return this.em.find(Order.class, orderId, hints);
What’s happening inside?
As in the previous post, we want to have a look at the hibernate log and find out what hibernate is doing. As we can see, the result of a dynamic entity graph is the same as of a named entity graph. It creates a load plan and one select statement with all 3 entities.
2014-04-07 20:08:15,260 DEBUG [org.hibernate.loader.plan.build.spi.LoadPlanTreePrinter] (default task-2) LoadPlan(entity=blog.thoughts.on.java.jpa21.entity.graph.model.Order) - Returns - EntityReturnImpl(entity=blog.thoughts.on.java.jpa21.entity.graph.model.Order, querySpaceUid=, path=blog.thoughts.on.java.jpa21.entity.graph.model.Order) - CollectionAttributeFetchImpl(collection=blog.thoughts.on.java.jpa21.entity.graph.model.Order.items, querySpaceUid=, 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=, path=blog.thoughts.on.java.jpa21.entity.graph.model.Order.items.) - QuerySpaces - EntityQuerySpaceImpl(uid=, 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)) : -> - CollectionQuerySpaceImpl(uid=, 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)) : -> - EntityQuerySpaceImpl(uid=, entity=blog.thoughts.on.java.jpa21.entity.graph.model.OrderItem) - SQL table alias mapping - items1_ - alias suffix - 2_ - suffixed key columns - {id1_0_2_} 2014-04-07 20:08:15,260 DEBUG [org.hibernate.loader.entity.plan.EntityLoader] (default task-2) 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_ from purchaseOrder order0_ left outer join OrderItem items1_ on order0_.id=items1_.order_id where order0_.id=?
Conclusion
After defining a named entity graph in the first post, we now used the EntityGraph API to define an dynamic entity graph. Using this entity graph, we can fetch a graph of multiple entities with only one query from the database. This can be used to solve LazyInitializationException and to improve the performance applications.
What do you think about (dynamic) entity graphs? From my point of view this is a very useful feature in JPA. Especially the dynamic entity graphs are useful to define your fetch strategy based on runtime information like method parameters.
If you want to learn more about the new interesting JPA features, have a look at my other articles:
- How to implement a Type Converter
- Type Converter – The better way to persist enums
- Criteria Update/Delete – The easy way to implement bulk operations with
- Entity Graph – Part 1: Named entity graphs
it is possible to use entity graph with refresh()?
Hi Tarcisio,
No, unfortunately, you can’t use them with refresh().
Regards,
Thorben
Has anyone written a utility to generate the graph and map in one function by passing in the Classes of objects that are to be graphed?
I like this implementation, but if you have two or more levels of sub entities then the graph creation can get very ugly.
I’m not aware of such an implementation.
How would you handle the situation that there are 2 associations with the same type?
Hi Thorben ,
Entity Graph is a great feature introduced in JPA . I got solution for most of my problems where i need to fetch large data set with associations at once . But is it possible create dynamic entity graphs with multi level depth and fetch data ?
For example Instructor —ManyToMany–> Students —OneToMany-> Vehicle –OneToOne–> VehicleComapny
Will be really helpful if you can give me a code example to do this !
Regards ,
Vaneet Kataria
Hi Vaneet,
The addSubgraph method of the EntityGraph and Subgraph interfaces return a new Subgraph object. That enables you to create such a graph.
EntityGraph graph = this.em.createEntityGraph(Order.class); itemGraph = graph.addSubgraph(“items”);
Subgraph
itemGraph.addAttributeNodes(“product”);
Please be aware that huge entity graphs might cause performance problems because your query result gets huge.
And you should model your to-many association as a Set to avoid the MultipleBagFetchException (see Hibernate Tips: How to avoid Hibernate’s MultipleBagFetchException)
Regards,
Thorben
Hi Thorben,
I am using the JPA 2.1 . I am debugging the code and found that It’s not working. Whenever I expand a object in debug a query got fire. But my expectation is Query should not fire in the . It should be join fetch for Eager.
Please correct me if I missed or wrong anywhere.
Thanks,
Amit Yadav
It sounds like your EntityGraph wasn’t used or didn’t work correctly. Did you provide it as a hint to your query?
How can I use a dynamic entity graph in conjunction with a CrudRepository or similar? I can get a handle on the EntityManager, but that doesn’t help because I’m not calling em.find, so I can’t give it the hints!
I’m not sure if I understand your question. Do you want to use the EntityGraph with a query?
You can do that by calling the setHint method on the Query or TypedQuery interface.
TypedQuery q = em.createQuery(“SELECT a FROM Author a WHERE a.id = 1”, Author.class);
q.setHint(“javax.persistence.fetchgraph”, graph);
Author a = q.getSingleResult();
Regards,
Thorben
In this graph:
@NamedEntityGraph(name = “graph.Order.items”,
attributeNodes = @NamedAttributeNode(value = “items”, subgraph = “items”),
subgraphs = @NamedSubgraph(name = “items”, attributeNodes = @NamedAttributeNode(“product”)))
items and its products are already defined to be eager.. right?
Why when you decide to use this graph, you have to specify AGAIN what need/want to be fetched:
Subgraph itemGraph = graph.addSubgraph(“items”);
itemGraph.addAttributeNodes(“product”);
Makes no sense
These are 2 different ways to define the same graph.
The @NamedEntityGraph annotation provides an annotation-based approach to define the graph. This graph can’t be changed at runtime
The Graph API allows you to dynamically create the graph at runtime.
In the end, you can use both approaches to create the same graph. That’s what I did in this article.
We have a pretty large class hierarchy (table per class hierarchy using a discriminator column). When I run a query for a certain subclass, all the ManyToOne associations are being eagerly fetched ( at least 25 sql queries to various tables). I’m only looking for few properties that are spread across the class hierarchy. 2 properties in the parent class, 3 properties in a subclass. When I create an entity graph and try to fetch using the javax.persistence.fetchgraph property, I’m seeing the same number of sql queries. I’m trying to understand if by using the entityGraph approach shouldn’t I see fewer number of queries .. or what problem does this solve? Thanks
Yes, you should see fewer queries. If you configured and activated the graph correctly, Hibernate should use 1 query to fetch all entities and associations referenced in the graph.
But in your case, it seems to be better to create a query that only fetches the required attributes and map them to a DTO projection: Why, When and How to Use DTO Projections with JPA and Hibernate
Hello Thorben,
I really like this artikle. But as my company is still unsing JPA 2.0 and I am new in this field: Is everything also possible with JPA 2.0?
Hi Stephan,
Entity Graphs were introduced with JPA 2.1, so you can’t use them in 2.0 🙁
But you can use a JOIN FETCH statement instead: Hibernate Tips: How to initialize lazy relationships within a query
Regards,
Thorben
This might be true for hibernate-specific (“vendor-specific”) things, but how about for JPA? The JPA is a vendor-independent approach to overcome the implications and limitations you had with when you implement against a specific implementation (like hibernate is). Now we have the javax.persistence.* packages which form the JPA (independent from DBMS and persistence implementation).
Surely you already know the difference: Hibernate (vendor-specific) != JPA (vendor-independent). So let’s encourage people of moving away from a specific vendor and lead them towards generic JPA.
PS: Sorry when I may sound not polite, it was not intended (non-native English writer).
No, I have not seen it in a “real” project so far. But that will change as soon as critical applications switch to JEE7 application servers.
Having hundreds of these annotations might get a little messy 😉
But it would be similar with predefined SQL statements. You have to put the predefined stuff somewhere in the end.
The only alternative would be to create the statement at runtime, based on the user input. And with JPA 2.1 you could even create a dynamic entity graph for the query 😉
But creating everything at runtime has also several drawbacks…
Independent of your approach to define a database query, structuring a hughe application is not an easy job. And I don't think, that there is a one size fits all approach. You always need to find the best solution for your current application.
Regards,
Thorben
I'm really very curious where this whole Annotatiomania™ leads us. Once we declare something like a named entity graph with declarative tools like annotations, I really start wondering why we have stopped writing SQL.
I understand that this whole set of features will go through a variety of caches in Hibernate and leverage the whole platform. But the sheer complexity of a project that yields hundreds of such entity graphs makes me think if querying shouldn't better be done with the optimal query language for RDBMS.
Have you already seen these JEE7 things in the wild?