Hibernate Tips: How to avoid Hibernate’s MultipleBagFetchException

By Thorben Janssen

Association Mapping, Mapping, Query

Hibernate Tips is a series of posts in which I describe a quick and easy solution for common Hibernate questions. If you have a question for a future Hibernate Tip, please leave a comment below.

Question:

You explained that I should use a JOIN FETCH clause to initialize all associations of my entity that I will use in my use case. But as soon as I do that for more than 1 association, Hibernate throws a MultipleBagFetchException.

How can I JOIN FETCH multiple associations?

Solution:

Hibernate throws a MultipleBagFetchException whenever you try to fetch multiple Bags in a query.

org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple Bags: [org.thoughts.on.java.model.Book.authors, org.thoughts.on.java.model.Book.reviews]

As I explained in my post about the most efficient data type for to-many associations, a Bag is an unordered collection. Hibernate uses it if you model your association as a List.

@Entity
public class Book {
	
	// Don't do this!

	@ManyToMany
	@JoinTable(
		      name="BookAuthor",
		      joinColumns={@JoinColumn(name="bookId", referencedColumnName="id")},
		      inverseJoinColumns={@JoinColumn(name="authorId", referencedColumnName="id")})
	private List authors = new ArrayList();
	
	@OneToMany(mappedBy = "book")
	private List reviews = new ArrayList();
	
	...
	
}

You can avoid the MultipleBagFetchException by using a Set instead of a List.

@Entity
public class Book {
	
	@ManyToMany
	@JoinTable(
		      name="BookAuthor",
		      joinColumns={@JoinColumn(name="bookId", referencedColumnName="id")},
		      inverseJoinColumns={@JoinColumn(name="authorId", referencedColumnName="id")})
	private Set authors = new HashSet();
	
	@OneToMany(mappedBy = "book")
	private Set reviews = new HashSet();
	
	...
	
}

In contrast to the previously used Bags, Hibernate fetches multiple Sets without throwing an exception.

Book b = em.createQuery("SELECT b "
				+ "FROM Book b "
					+ "JOIN FETCH b.authors a "
					+ "JOIN FETCH b.reviews r "
				+ "WHERE b.id = 1",
				Book.class).getSingleResult();

Learn more:

I explained the differences between a Set and a Bag in more details in How to Choose the Most Efficient Data Type for To-Many Associations – Bag vs. List vs. Set.

And if you want to learn more about Hibernate associations and how you can handle them efficiently, you should read the following posts:

 

Hibernate Tips Book


Get more recipes like this one in my new book Hibernate Tips: More than 70 solutions to common Hibernate problems.

It gives you more than 70 ready-to-use recipes for topics like basic and advanced mappings, logging, Java 8 support, caching and statically and dynamically defined queries.

Get it now as a paperback, ebook or PDF.

Tags

Association Mapping, Mapping, Query


About the author

Thorben is an independent consultant, international speaker, and trainer specialized in solving Java persistence problems with JPA and Hibernate.
He is also the author of Amazon’s bestselling book Hibernate Tips - More than 70 solutions to common Hibernate problems.

Books and Courses

Coaching and Consulting

Leave a Repl​​​​​y

Your email address will not be published. Required fields are marked

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  1. Hi,
    this tip avoids the exception, but findBy may then return wrong results!

    I had a simular situation with two eager associations. Translated to book example: with a book b, having
    two authors a1 and a2 and two reviews r1 and r2.

    findBy returned b having two reviews r1 and r2 but four authors a1, a2, and again a1, a2!
    The problem seams to be an, in this case, incorrect generated sql like:
    select . from book left outer join author . left outer join review ..

    Looks to be an error in Hibernate, to use a list and a set together should also be forbidden?!

    1. Hi Hans-Martin,

      I wasn’t able to reproduce that issue. But what happens is, that you will get the Book entity 4 times, if it’s associated with 2 Authors and 2 Reviews. You can avoid that by adding the DISTINCT keyword to your query and by setting the QueryHints.PASS_DISTINCT_THROUGH to false, e.g.:

      TypedQuery q = em.createQuery(“SELECT DISTINCT b ”
      + “FROM Book b ”
      + “JOIN FETCH b.authors a ”
      + “JOIN FETCH b.reviews r ”
      + “WHERE b.id = 1”,
      Book.class);
      q.setHint(QueryHints.PASS_DISTINCT_THROUGH, false);
      List
      b = q.getResultList();

      Regards,
      Thorben

  2. If you change the collection to a Set how will you be able to add multiple new entities to the collection? Even if you add them one by one and have a save call to the child entity, you will run into a problem if the entity manager does not flush between the calls to add the new entities to the parent entity.

    1. Hi Om Tara,

      That depends on the equals() implementation of your entity. In most cases, I don’t recommend implementing equals. But if you do, you need to make sure that an entity object with a null primary key value is never equal to another entity object.

      Regards,
      Thorben

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}