JPA OneToMany : List vs Set

后端 未结 2 1401
有刺的猬
有刺的猬 2021-01-31 10:19

I have two entities: UserAccount and Notification. These have a relationship as shown below.

 public class UserAccount {

    @Id
    @         


        
相关标签:
2条回答
  • 2021-01-31 10:35

    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

    0 讨论(0)
  • 2021-01-31 10:44

    List: Allows duplicate elements in it.

    Set: All elements should be unique.

    Now, delete may be happening because you are over-writing element in list, and so when you modify persisted entity of UserAccount type, it is removing the entity which is in list previously.

    0 讨论(0)
提交回复
热议问题