While working on a side project using a JAX-RS API and JPA I wanted to explore how the entity graph features
introduced in JPA 2.1 could be used to simplify data fetches for different endpoints. One issue I quickly ran into
was how to deal with
LazyInitializationExceptions with unloaded lazy fields.
The Hibernate module supplied by Jackson didn’t meet my needs and I couldn’t find any examples of what I needed so it was time to delve into how Jackson filtering works. The end solution turned out to be fairly straightforward and doesn’t rely on any vendor specifics so it’s worth sharing.
The original project was written in Scala but I have converted the code to Java for this example. The project and example have both been written for Wildfly 20 and Jackson 2.11 but should work on any Java/Jakarta EE 8+ server.
Initial model and resource
We are going to start with a very simple
That has a collection of
Fetching and returning
Articles is handled by a simple JAX-RS resource:
Calling this endpoint returns a single
article with an embedded list of
Other boilerplate such as configuring JAX-RS, JPA, and Jackson are omitted - we are using the defaults for these.
Making tags lazy
Now we want to implement a requirement to only fetch tags if the API request explicitly requests them with an
include_tags query parameter. The entity graph support in JPA 2.1 is an ideal fit for this feature as
allows switching the fetch behaviour without needing to change the query.
We start by adding a
NamedEntityGraph to the
Article entity and changing the fetch mode of
tags back to the
The new query parameter can be added to the JAX-RS endpoint and the
find call extended to use the new entity graph:
Calling the endpoint with the new query parameter set (
include_tags=true) works as expected and returns the same
JSON as before. But if we remove the query parameter we get a 500 response and an ugly stack trace from Hibernate as
the tags have not been fetched:
Jackson provides a solution to this with their Hibernate modules  which will detect Hibernate proxy
objects that are not initialised and handle them for us. If we enable the
Hibernate5Module and retry the request we
get this response:
No stack traces and a valid JSON response - great! But the response has
"tags": null which implies that there are no
tags defined. We know that there are tags by making a request with
include_tags=true so having an explicit
value here is misleading - a better solution would be to omit the
tags field entirely.
Filtering lazy fields
Jackson includes a PropertyFilter interface that can be used to dynamically exclude properties at runtime. We can set up a filter to detect lazy fields and omit the property if they haven’t been loaded:
PersistenceUtil.isLoaded is a new feature added in JPA 2.0 that returns
true if an
entity has been fetched or is not subject to lazy loading.
This new filter gets wired into the
And finally added to the
Now when we make a lazy request we get a response with the
tags field omitted:
The Jackson Hibernate modules are a great fit for many situations but they don’t handle the scenario where you want to
completely omit lazy fields. Thankfully it is easy to implement a
PropertyFilter that uses
exclude unloaded lazy fields.
JPA 2.1 entity graphs are fantastic for handling lazy-loading across several JAX-RS endpoints. A set of entity graphs
can be easily combined and used by multiple queries, dramatically simplifying fetch code. The only gripe so far
is the way that entity graphs are selected using query hints - I feel that extending the API for
TypedQuery) would have been a cleaner solution.
Full example code for this post can be found on GitHub.
For a real-world application this would more likely be including tags on a show endpoint and excluding them on the list endpoint. ↩︎
Hibernate5Moduledepending your Hibernate version. ↩︎
This filter could be also added to a base entity class or applied globally using a Jackson mixin class. ↩︎