JPA OneToMany : List vs Set

I faced this problem not so long ago…

I found this article: Performance Antipatterns of One To Many
Association in Hibernate https://fedcsis.org/proceedings/2013/pliks/322.pdf

in short:

  • Bag semantics -> List / Collection + @OneToMany -> One Element Added: 1 delete, N inserts , One Element Removed: 1 delete, N inserts
  • List semantics -> List + @OneToMany + @IndexColumn / @OrderColumn -> One Element Added: 1 insert, M updates, One Element Removed: 1 delete, M updates
  • Set semantics -> Set + @OneToMany -> One Element Added: 1 insert , One Element Removed: 1 delete

For me: yes that mean that you have to change your List to Set for unidirectional @OneToMany. So I changed my model to match with Hibernate expectations and that cause a lot of issues because the view part of the application was relying on List mostly…

In one hand the Set is a logical choice for me because there are no duplications, in the other hand List were easier to deal with.

So JPA/Hibernate forced me to change the model object and that was not the first time, when you are using @EmbededId you do something that you probably won’t do in the same way without JPA/Hibernate. And when you have to be aware of HibernateProxy in all the application especially in equals methods … else if(object instanceof HibernateProxy) { …, you notice the JPA/Hibernate persitence layer is a little bit intrusive in others layers.

But when I use directely JDBC I also use to change the model or the buisness methods to facilitate the persistence…
Layers isolation is may be a dream or cost too much to be done at 100%?

And you can order a Set if they are SortedSet like TreeSet with the annotation @OrderBy

That bring a problem when some code rely on List and cannot be changed (such as JSF/PrimeFaces <dataTable> or <repeat> components)
So you have to change your Set into List and go back to Set but if you do setNotifications(new HashSet<>(notificationList)) you will have extra queries because the set is a org.hibernate.collection.PersistentSet managed by Hibernate… So I used addAll() and removeAll() instead of setters:

protected <E> void updateCollection(@NonNull Collection<E> oldCollection, @NonNull Collection<E> newCollection) {
    Collection<E> toAdd = new ArrayList<>(newCollection) ;
    toAdd.removeAll(oldCollection) ;

    Collection<E> toRemove = new ArrayList<>(oldCollection) ;
    toRemove.removeAll(newCollection) ;

    oldCollection.removeAll(toRemove) ;
    oldCollection.addAll(toAdd) ;
}

Mind the equals() and hashCode() methods of your @Entity

One other problem is that you need to Master both JPA and Hibernate if you want to use JPA with Hibernate as the implementation because the Set/List/Bag semantic is from Hibernate not from JPA (correct me if I’m wrong)

A specification is made for abstracting the implementation to not depend on one specific vendor. Although most of JavaEE specs succed, JPA failed for me and I gave up to be independent of Hibernate

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)