JPA Entity Graphs

posted by Roberto Cortez on

One of the latest features in JPA 2.1 is the ability to specify fetch plans using Entity Graphs. This is useful since it allows you to customize the data that is retrieved with a query or find operation. When working with mid to large size applications is common to display data from the same entity in different and many ways. In other cases, you just want to select a smallest set of information to optimize the performance of your application.

You don’t have many mechanisms to control what is loaded or not in a JPA Entity. You could use EAGER / LAZY fetching, but these definitions are pretty much static. You were unable to change their behaviour when retrieving data, meaning that you were stuck with what was defined in the entity. Changing these in mid development is a nightmare, since it can cause queries to behave unexpectedly. Another way to control loading is to write specific JPQL queries. You usually end up with very similar queries and the following methods: findEntityWithX, findEntityWithY, findEntityWithXandY, and so on.

Before JPA 2.1, the implementations already supported a non standard way to load data similar to Entity Graphs. You have Hibernate Fetch Profiles, OpenJPA Fetch Groups and EclipseLink Fetch Groups. It was logical to have this kind of behaviour in the specification. It allows you a much finer and detail control on what you need to load using a standard API.

Example

Consider the following Entity Graph:

Movie Entity Graph

(Probably the relationships should be N to N, but lets keep it simple).

And the Movie Entity has the following definition:

Looking closer to the entity, we can see that we have three 1 to N relationships and movieDirectors is set to be Eagerly loaded. The other relationships are set to the default Lazy loading strategy. If we want to change this behaviour, we can define different loading models by using the annotation @NamedEntityGraph. Just set a name to identify it and then use the @NamedAttributeNode to specify which attributes of the root entity that you want to load. For relationships you need to set a name to the subgraph and then use @NamedSubgraph. In detail:

Annotations

This defines an Entity Graph with name movieWithActors and specifies that the relationship movieActors should be loaded.

This defines an Entity Graph with name movieWithActorsAndAwards and specifies that the relationship movieActors should be loaded. Additionally, it also specifies that the relationship movieActors should load the movieActorAwards.

Note that we don’t specify the id attribute in the Entity Graph. This is because primary keys are always fetched regardless of what’s being specified. This is also true for version attributes.

Hints

To use the Entity Graphs defined in a query, you need to set them as an hint. You can use two hint properties and these also influences the way the data is loaded.

You can use javax.persistence.fetchgraph and this hint will treat all the specified attributes in the Entity Graph as FetchType.EAGER. Attributes that are not specified are treated as FetchType.LAZY.

The other property hint is javax.persistence.loadgraph. This will treat all the specified attributes in the Entity Graph as FetchType.EAGER. Attributes that are not specified are treated to their specified or default FetchType.

For more information, please refer to the sections 3.7.4.1 – Fetch Graph Semantics and 3.7.4.2 – Load Graph Semantics of the JPA 2.1 specification.

To simplify, and based on our example when applying the Entity Graph movieWithActors:

Default / Specifiedjavax.persistence.fetchgraphjavax.persistence.loadgraph
movieActorsLAZYEAGEREAGER
movieDirectorsEAGERLAZYEAGER
movieAwardsLAZYLAZYLAZY

In theory, this should be how the different relationships are fetched. In practice, it may not work this way, because the JPA 2.1 specification also states that the JPA provider can always fetch extra state beyond the one specified in the Entity Graph. This is because the provider can optimize which data to fetch and end up loading much more stuff. You need to check your provider behaviour. For instance Hibernate always fetch everything that is specified as EAGER even when using the javax.persistence.fetchgraph hint. Check the issue here.

Query

Performing the query is easy. You do it as you would normally do, but just call setHint on the Query object:

To get the Entity Graph you want to use on your query, you need to call the getEntityGraph method on the EntityManager and pass the name. Then use the reference in the hint. Hint must be either javax.persistence.fetchgraph or javax.persistence.loadgraph.

Programmatic

Annotations may become verbose, especially if you have big graphs or many Entity Graphs. Instead of using annotations, you can programmatically define Entity Graphs. Let’s see how:

Start by adding a static meta model Entity Class:

This is not really needed, you can reference the attributes by their string names, but this will give you type safety.

This Entity Graph specifies that all relationships of the Entity must be loaded. You can now adjust to your own use cases.

Resources

You can find this sample code in the Java EE samples at Github. Check it here.

Extra Note: currently there is a bug in EclipseLink / Glassfish that prevents javax.persistence.loadgraph hint from working properly. Check the issue here.

Conclusion

Entity Graphs filled a gap missing in the JPA specification. They are an extra mechanism that helps you to query for what you really need. They also help you to improve the performance of your application. But be smart when using them. There might be a better way.

Comments ( 34 )

  1. ReplyKlacia

    Enity Graphs are great, but there is a lot of bugs in EclipseLink and Hibernate implementations. As far as I know it doesn’t work using different dialects / databases in Hibernate. We tried using it with Oracle and hibernate generates SQL queries with incorrect syntax. EclipseLink also have problems but I dont remember what exacly was wrong.

    • ReplyRoberto Cortez

      Hi Klacia,

      Yes, Entity Graphs are not bug free yet. Even with very simple examples, I just found two issues that I described in the post. Since there are multiple combinations of queries x databases, it’s natural that maybe some corner cases are having problems. The best thing it to report these problem to the implementations so they can fix them 🙂

      Thank you for reading.

  2. ReplyArjan Tijms

    Great article!

    We actually bumped into the same issue that unspecified attributes are loaded anyway, which IMHO greatly diminishes the value of entity graphs for certain use cases.

    I have just created an issue at the JPA spec JIRA in response to this, see JPA_SPEC-96

    • ReplyRoberto Cortez

      Hi Arjan, thank you for your feedback!

      I couldn’t agree more with JPA_SPEC-96. Great idea to submit that improvement.

  3. ReplyLukas Eder

    Eek, Roberto! I hereby award you the title Annotatiomaniac of the Year. Congratulations! You’re sure glad those annotations keep you from writing tedious SQL, right? 😉

    • ReplyRoberto Cortez

      Hey Lukas,

      I’ll wear that title proudly 🙂

      I still write tons of SQL 😛

  4. Replysimas_ch

    I see the idea behind EntityGraphs. But in my opinion they are only needed when you use JPA in a naive way and create a one to one mapping of your database to entities.

    In most situation where you only use JPA for reading you better create DTOs using the constructor expression (or plain SQL).

    And in reality from the application perspective a one to one mapping is just not needed.

    • ReplyRoberto Cortez

      Hi Simon,

      I don’t think that Entity Graphs is the answer to every situation, but it’s a nice to have mechanism in your tool box to fix problems.

  5. ReplyCristhian

    Hi,

    I started using entity graphs but when I try to load a lazy collection JPA returns repeated result, I understand that this is because of the JOIN , but how can avoid it and tell JPA that only load the collection without repeat the owner entity ??.

    Thanks,

    Cristhian.

  6. ReplyMwanji

    Has anyone gotten an entity graph to work with a query with multi-valued named or positional parameters?

    For example: SELECT o FROM Order o WHERE o.company IN :companies

    This query works fine without an entity graph, but when I add an entity graph hint, Hibernate fails to bind the named parameter. If the query parameter is single-valued (ie. WHERE o.company = :company), it works. Positional parameters fail with a different exception.

    • ReplyRoberto Cortez

      Hi Mwanji,

      I’ve never tried that before, but I was able to reproduce your problem. Seems like an Hibernate bug. It does work on Glassfish / Eclipse Link. I’m pushing test samples in https://github.com/javaee-samples/javaee7-samples/tree/master/jpa/entitygraph

      We need to fill a bug in Hibernate if there isn’t one already. I couldn’t find one at least.

      • ReplyMwanji

        Thanks for trying it out and confirming, Roberto. I’ll try to file a Hibernate bug sometime soon.

        • ReplyRoberto Cortez

          No problem. I can fill the bug and point to the sample that reproduce it if you don’t have the time.

          • Mwanji

            That’s actually a good idea, since you’ve already done the work and I haven’t been able to file it yet :). I looked at the JIRA and got a bit intimidated by how many open bugs there already are…

          • Roberto Cortez

            I’ll do it then 🙂

          • Mwanji

            Awesome, thanks so much for filing it. I’ll vote up the other bug that you marked yours as a duplicate of as soon as Jira allows me to do so.

          • Roberto Cortez

            Cool. When I created the issue, I’ve searched for something similar, but couldn’t find. I only noticed a few days later. Cheers!

  7. ReplySankar

    In your programmatic example, why not add the associations “movieActor”, “movieAwards” and “movieDirector” as attribute nodes? Also in my case adding “movieActors” as a subgraph with the attribute node “movieActorAwards”, fetched the deeper association with Hibernate 4.3.7. Does this programmatic example fetches the “movieActorAwards” as it is?

    • ReplyRoberto Cortez

      Hi Sankar,

      I’ve could have used the attribute nodes indeed. I guess that I’ve used the subgraph so I could change the loading in that particular subgraph as well.

      Regarding the fetch of the node “movieActorAwards”, it always depends on the setting. As it is, no relationship is fetched because “movieActorAwards” defaults to LAZY. You would need to specified it to be fetched as an attribute or graph.

      Thank your for your comment.

  8. ReplyMani

    Hello robert,

    great article and thanks to you. I think you should also include
    “Hint providing a “fetchgraph” EntityGraph. Attributes explicitly specified as AttributeNodes are treated as FetchType.EAGER (via join fetch or subsequent select). Note: Currently, attributes that are not specified are treated as FetchType.LAZY or FetchType.EAGER depending on the attribute’s definition in metadata, rather than forcing FetchType.LAZY” on fetch graph and “Hint providing a “loadgraph” EntityGraph. Attributes explicitly specified as AttributeNodes are treated as FetchType.EAGER (via join fetch or subsequent select). Attributes that are not specified are treated as FetchType.LAZY or FetchType.EAGER depending on the attribute’s definition in metadata” on loadgraph hints because reading at your article I once thought attributes that are not included will be forced to be lazy on fetch hint regardless of its metadata defined.

    • ReplyRoberto Cortez

      Thank you Mani,

      I’ve updated the post with your comments 🙂

  9. ReplyMarkus

    Hi Roberto,

    Now the description of the fetch type hints is confusing; according to the JPA 2.1 Spec section 3.7.4:

    javax.persistence.fetchgraph: “attributes that are not specified are treated as FetchType.LAZY”
    javax.persistence.loadgraph: “attributes that are not specified are treated according to their specified or default FetchType”

    • ReplyRoberto Cortez

      Hi Markus,

      I agree that the wording got a little confusing. I simplified the description and added a reference to the specification sections for additional information.

      Thank you,

  10. ReplyDmytro

    As for me JPA query is a more concise way than “NamedEntityGraph” annotation approach if you want to fetch an object graph. Anyway great article and thanks U very match!

    • ReplyRoberto Cortez

      Indeed, for complex queries the annotation based API is a mess.

  11. ReplyJPA: Speichern von Entities – HZ-Jade Blog

    […] JPA Entity Graphs […]

  12. ReplyRoland

    Is the bug still an issue? I have a complex entity graph with 25 entities (+2 tables as they are pure join tables). I use Payara which uses EclipseLink. Each foreign entity seem to be fetched (I use default, so EAGER) distinctly (with a WHERE on primary key) which can create a huge load when you have 10,000+ records in all your entities and maybe “only” 100 active users.

  13. ReplySurya

    What if I want to write a query to fetch all the movie details with where clause for movieactorward How can I write this query . Please help

  14. ReplySpring Boot Jpa JPQL选择除特定列之外的列 - 4Find

    […] Here is另一篇有关实体图的不错的文章 […]

Leave a Reply to Arjan Tijms Cancel reply

Your email address will not be published.

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>