• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar

Thoughts on Java

  • Tutorials ▼
    • Hibernate & JPA Tutorials
    • Hibernate Tips
    • Tutorials on YouTube
  • Books & Courses ▼
    • Hibernate Tips Book
    • Online Training
    • Open Workshops
    • On-Site Training
  • Consulting ▼
    • Consulting Call
    • Project Coaching
  • About ▼
    • About Thoughts on Java
    • Support Thoughts on Java
    • Resources
  • Member Library
You are here: Home / Hibernate / Hibernate Performance / How to Choose the Most Efficient Data Type for To-Many Associations – Bag vs. List vs. Set
Do you live in Europe?
Then I hope to see you at one of my in-person workshops in December 2019. You will learn to use Hibernate advanced features and to improve the performance of your persistence layer.

How to Choose the Most Efficient Data Type for To-Many Associations – Bag vs. List vs. Set

By Thorben Janssen 3 Comments

  • tweet 
  • share 
  • share 
  • share 
  • share 
  • email 


Which data type should you use to map a to-many association with Hibernate? Is it better to use a Set or a List?

That’s a very common question, and most developers are surprised when they look at the documentation and find out that these are not the only options. You can also use a Bag or a Map.

The mapping to a Map is a special case, and I already explained it in great details in one of my previous posts. In contrast to the other 3 options, it provides indexed based access to the associated entities. That might be beneficial for some use cases, but the creation and management of the Map also creates an overhead. It should, therefore, not be your default choice.

So, let’s focus on the other 3 options. List, Set, and Bag represents simple collections of entities. The first 2 options should sound familiar because the java.util package contains a List and a Set interface.

But what is a Bag? There is no class in the java.util package with that name.

Don’t want to read? You can watch it here!

Follow me on YouTube to not miss any new videos.

The difference between a Bag and a List

Hibernate’s naming of the different collection types is a little bit confusing because Lists and Bags are both mapped by a java.util.List. The difference between them is that a List is ordered and a Bag is unordered.

So, if you map your to-many association to a java.util.List without specifying the order of your association elements, you’re using a Bag and not a List. That should be the case for most of your associations because retrieving the association in a specific order slows down your database queries. You should better use a JPQL query with an ORDER BY clause to define the ordering if you need it.

So, for most association mappings, there are 2 options remaining. You need to decide between a Bag and a Set.


Already a member? Login here.


 

Should you use a Bag or a Set?

When you just look at the Java types, the answer seems to be easy. In general, a java.util.List provides the better performance while a java.util.Set doesn’t contain any duplicates. As long as you implement the create use case correctly, a java.util.List seems like the obvious best choice for your association mapping.

But it’s not that easy. A List might be more efficient than a Set, but the type also influences how Hibernate manages the association in the database. So, there are a few other things you need to take into account when you make your decision.

 

A critical bug in older Hibernate versions

First of all, if you’re using a Hibernate version older than 5.0.8, you should be aware of bug HHH-5855. When you used a java.util.List and merged the parent entity, Hibernate generated 2 INSERT statements for each new child entity.

 

Inefficient handling of many-to-many associations

When you’re mapping a many-to-many association, you should always use a java.util.Set.

 

Don’t use a List for many-to-many associations

If you model the association as a java.util.List, Hibernate handles the removal of associated entities very inefficiently.

@Entity
public class Book {

	// DON'T DO THIS!!!
	@ManyToMany
	@JoinTable(name = "book_author", 
			joinColumns = { @JoinColumn(name = "fk_book") }, 
			inverseJoinColumns = { @JoinColumn(name = "fk_author") })
	private List authors = new ArrayList();
	
	...
}

In the following code snippet, I load a Book which was written by 2 Authors and remove one of the Authors from the association.

em = emf.createEntityManager();
em.getTransaction().begin();

// Get Book entity with 2 Authors
b = em.find(Book.class, 1L);

// Remove one of the Author
b.getAuthors().remove(a);

em.getTransaction().commit();
em.close();

As you can see in the log messages, Hibernate removes all records from the association table before it inserts a new record for the remaining association.

This approach is obviously very inefficient. Depending on the number of associated entities, the additional INSERT statements can create performance problems.

...
09:54:28,876 DEBUG [org.hibernate.SQL] - update Book set title=?, version=? where id=? and version=?
09:54:28,878 DEBUG [org.hibernate.SQL] - delete from book_author where fk_book=?
09:54:28,882 DEBUG [org.hibernate.SQL] - insert into book_author (fk_book, fk_author) values (?, ?)

 

Use a Set to map many-to-many associations

Hibernate handles the association a lot better if you model it as a java.util.Set.

@Entity
public class Book {

	@ManyToMany
	@JoinTable(name = "book_author", 
			joinColumns = { @JoinColumn(name = "fk_book") }, 
			inverseJoinColumns = { @JoinColumn(name = "fk_author") })
	private Set authors = new HashSet();
	
	...
}

If you run the same test case again, Hibernate now only removes the record that represents the removed association. As expected, all other database records are not affected by the remove operation.

...
10:00:37,709 DEBUG [org.hibernate.SQL] - update Book set title=?, version=? where id=? and version=?
10:00:37,711 DEBUG [org.hibernate.SQL] - delete from book_author where fk_book=? and fk_author=?

 


Already a member? Login here.

Summary

As you’ve seen, mapping an association as a java.util.List can create problems which by far outweigh the small performance gain you get compared to a java.util.Set. So, better make sure to update your Hibernate version and to use a Set to model many-to-many associations.

  • tweet 
  • share 
  • share 
  • share 
  • share 
  • email 

Related posts:

  1. 7 Tips to boost your Hibernate performance
  2. 5 Common Hibernate Mistakes That Cause Dozens of Unexpected Queries
  3. Entity Mappings: Introduction to JPA FetchTypes
  4. Hibernate Tips: How to map a bidirectional many-to-many association
Become a Thoughts on Java Supporter to claim your member perks and to help me write more articles like this.

Filed Under: Hibernate Performance Tagged With: Association Mapping, Mapping

Improve Your Hibernate Skills At An In-Person Workshop

Implement Your Persistence Layer with Ease

Learn More About Hibernate

Need Some Help with Your Project?

Reader Interactions

Comments

  1. Thomas says

    January 8, 2018 at 5:17 pm

    Thanks Thorben. This post is very helpful

    Reply
    • Thorben Janssen says

      January 9, 2018 at 5:32 pm

      Thanks Thomas

      Reply
  2. Vipul Suman says

    May 8, 2019 at 8:35 am

    Great! I should try this.

    Reply

Leave a Reply Cancel reply

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.

Primary Sidebar

Join over 10.000 developers
in the
Thoughts on Java Library

Ebooks Sidebar Get free access to ebooks, cheat sheets and training videos.
Join Now!
Don't like ads?
Become a Thoughts on Java Supporter.
Special Launch Price

Let’s Connect


Thorben Janssen
Independent consultant, trainer and author
  • Facebook
  • GitHub
  • LinkedIn
  • Twitter
  • xing
  • YouTube

Speaking at

2nd-3rd December 2019
Düsseldorf (Germany):
Advanced Hibernate Workshop (2-day Workshop - English)

4th-6th December 2019
Düsseldorf (Germany):
Hibernate Performance Tuning Workshop (3-day Workshop - English)

Looking for an on-site training?

Featured Post

14 YouTube Channels You Should Follow in 2019

Getting Started With Hibernate

Entities or DTOs – When should you use which projection?

Ultimate Guide – Association Mappings with JPA and Hibernate

Ultimate Guide to JPQL Queries with JPA and Hibernate

Recent Posts

  • Don’t expose your JPA entities in your REST API
  • Hibernate Tip: How to control cache invalidation for native queries
  • Hibernate Tip: How to customize the association mappings using a composite key
  • Hibernate Tip: How to integrate an external query builder
  • Hibernate Tip: Best Way To Work with Scalar Projections
  • 6 Hibernate Mappings You Should Avoid for High-Performance Applications
  • Hibernate Tip: Best Way To Work with Scalar Projections
  • Using the Optimal Query Approach and Projection for JPA and Hibernate
  • Implementing the Outbox Pattern with CDC using Debezium
  • Hibernate Tip: Difference between @JoinColumn and @PrimaryKeyJoinColumn
Don't like ads?
Become a Thoughts on Java Supporter.

Copyright © 2019 Thoughts on Java

  • Impressum
  • Disclaimer
  • Privacy Policy