JPA OneToMany : List vs Set

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

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

 public class UserAccount {

    @Id
    @         


        
2条回答
  •  梦毁少年i
    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 or 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  void updateCollection(@NonNull Collection oldCollection, @NonNull Collection newCollection) {
        Collection toAdd = new ArrayList<>(newCollection) ;
        toAdd.removeAll(oldCollection) ;
    
        Collection 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

提交回复
热议问题