Introduction
In this article, I’m going to show you how to handle the MultipleBagFetchException thrown by Hibernate upon simultaneously fetching multiple collections with Spring Data JPA.
MultipleBagFetchException
As I explained previously in this article, the MultipleBagFetchException
is thrown by Hibernate when you try to fetch multiple List
collections at once.
By trying to fetch multiple one-to-many or many-to-many associations at once, a Cartesian Product is generated, and, even if Hibernate didn’t throw the MultipleBagFetchException
, we would still want to avoid getting a Cartesian Product in our query result set.
Domain Model
Let’s assume we have a Post
parent entity that has a bidirectional @OneToMany
association with the PostComment
entity and a unidirectional @ManyToMany
association with the Tag
entity:
The Post entity has a comments collection and a tags collection, like this:
|
|
Our goal is to fetch a number of Post
entities along with their associated comments
and tags
collections.
Getting a MultipleBagFetchException using a Spring Data JPA Query annotation
The first approach one would take is to create a @Query
method that uses JOIN FETCH
on both the comments
and tags
collections, like in the following example:
|
|
But, if you try to do that, your Spring application will not even start, throwing the following MultipleBagFetchException
upon trying to create the JPA TypedQuery
from the associated @Query
annotation:
|
|
How to fix the MultipleBagFetchException using a Spring Data JPA
So, while we can’t fetch both collections using a single JPA query, we can definitely use two queries to fetch all the data we need.
|
|
The findAllWithComments
query will fetch the desired Post
entities along with their associated PostComment
entities, while the findAllWithTags
query will fetch the Post
entities along with their associated Tag
entities.
Executing two queries will allow us to avoid the Cartesian Product in the query result set, but we’d have to aggregate the results so that we return a single collection of Post
entries that contain both the comments
and tags
collections initialized.
And that’s where the Hibernate First-Level Cache or Persistence Context can help us achieve this goal.
The PostService
defines a findAllWithCommentsAndTags
method that’s implemented as follows:
|
|
Since the @Transactional
annotation is placed at the class level, all methods will inherit it. Therefore, the findAllWithCommentsAndTags
service method is going to execute in a transactional context, meaning that both PostRepository
method calls will happen in the context of the same Persistence Context.
For this reason, the findAllWithComments
and findAllWithTags
methods will basically return two List
objects containing the very same Post
object references since you can have at most one entity reference managed by a given Persistence context.
While the findAllWithComments
method is going to fetch the Post
entities and store them in the Persistence Context or First-Level Cache, the second method, findAllWithTags
, will just merge the existing Post
entities with the references fetched from the DB that now contain the tags
collections initialized.
This way, both the comments
and the tags
collections are going to be fetched prior to returning the List
of Post
entities back to the service method caller.
In our integration test, we can verify that both collections have been initialized:
As you can see, we can read the size of the comments
and tags
collections even after the Persistence Context was closed since they’ve been fetched by the two entity query executed by the findAllWithCommentsAndTags
service method.
Awesome, right?
Conclusion
Knowing how to deal with the MultipleBagFetchException
is very important when using Spring Data JPA, as eventually, you are going to bump into this issue.
By fetching at most one collection per query, not only that you can prevent this issue, but you will also avoid the SQL Cartesian Product that would be generated when executing a single SQL query that JOINs multiple unrelated one-to-many associations.
Reference https://vladmihalcea.com/spring-data-jpa-multiplebagfetchexception/