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

Thoughts on Java

  • Tutorials ▼
    • Hibernate & JPA Tutorials
    • Hibernate Tips
    • Tutorials on YouTube
  • Books & Training ▼
    • Hibernate Tips Book
    • Online Training
    • 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
Special thanks to all Thoughts on Java Supporters for supporting this article!

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

By Thorben Janssen 2 Comments

  • tweet 
  • share 
  • share 
  • share 
  • share 
  • share 
  • e-mail 


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 
  • share 
  • e-mail 

Related posts:

  1. 5 Common Hibernate Mistakes That Cause Dozens of Unexpected Queries
  2. Ultimate Guide – Association Mappings with JPA and Hibernate
  3. Hibernate Tips: How to model an association that doesn’t reference primary key columns
  4. Hibernate Tips: Easiest way to manage bi-directional associations
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

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

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
  • Google+
  • LinkedIn
  • Twitter
  • xing
  • YouTube

Speaking at

7th February 2019
JUG Ostfalen (Germany):
Hibernate Tips 'n' Tricks: Typische Probleme schnell gelöst (Talk - German)

19th March 2019
JavaLand 2019 (Germany):
Hibernate Tips 'n' Tricks: Typische Probleme schnell gelöst (Talk - German)

21st March 2019
JavaLand 2019 (Germany):
Hibernate + jOOQ + Flyway = Die besten Frameworks in einem Stack (1-day Workshop - German)

6th-10th May 2019
JAX 2019 (Germany):
Hibernate Workshop: Komplexe Lösungen jenseits von CRUD (2-day Workshop - German)

6th-10th May 2019
JAX 2019 (Germany):
Spring Data JDBC vs. Spring Data JPA – Wer macht das Rennen? (Talk - German)

6th-10th May 2019
JAX 2019 (Germany):
Microservices mit Hibernate – typische Probleme und Lösungen (Talk - German)

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

  • Hibernate Tips: How to Customize a Constructor Expression for Different Subclasses
  • Hibernate Tips: How to Map java.time.Year with JPA and Hibernate
  • Why, When and How to Use DTO Projections with JPA and Hibernate
  • Hibernate Tip: Map a bidirectional one-to-one association with shared composite primary key
  • Implementing the Repository pattern with JPA and Hibernate
  • Hibernate Tips: How to Handle NULL Values while Ordering Query Results in a CriteriaQuery
  • 6 Hibernate Best Practices for Readable and Maintainable Code
  • Can you use Hibernate/EclipseLink/JPA for your microservice?
  • 14 YouTube Channels You Should Follow in 2019
  • Hibernate Tips: How To Map an Entity to a Query
Don't like ads?
Become a Thoughts on Java Supporter.

Copyright © 2019 Thoughts on Java

  • Impressum
  • Disclaimer
  • Privacy Policy