2012-10-27 51 views
15

假設我有兩個實體USER和NOTIFICATION。我有一個如下所示的關係。JPA OneToMany:List vs Set

public class UserAccount{ 

    @Id 
    @Column(name = "USER_NAME") 
    private String emailid; 

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) 
    @JoinTable(name = "USERS_NOTIFICATIONS", joinColumns = { @JoinColumn(name = "USER_NAME") }, inverseJoinColumns = { @JoinColumn(name = "NOTIFICATION_ID") }) 
    private List<Notification> notifications; 

    //setters, getter, equals and hashcode 
} 

我重寫了equals和hashcode(使用我的業務鍵/主鍵生成的IDE)。說我有用戶A.當我添加第一個通知時,我得到一個正常的插入語句。但是對於同一用戶的進一步添加,它會刪除並插入。這是日誌:

Hibernate: delete from USERS_NOTIFICATIONS where USER_NAME=? 
Hibernate: insert into USERS_NOTIFICATIONS (USER_NAME, NOTIFICATION_ID) values (?, ?) 
Hibernate: insert into USERS_NOTIFICATIONS (USER_NAME, NOTIFICATION_ID) values (?, ?) 
Hibernate: insert into USERS_NOTIFICATIONS (USER_NAME, NOTIFICATION_ID) values (?, ?) 
//as many inserts as the notifications the user has 

同樣的事情發生在每個用戶。現在,如果我用Set來替換列表,就會發生普通插入。我在閱讀doucmentationblog後找到原因。從文檔

  • 在單向一對多關聯集

    觀察是優選的。

它應該清楚的是索引的集合和集合允許在增加,刪除和修改元素中最有效的操作。

  • 在雙向一對多關係(多對一管理)列表和包是有效的。

Bag和list是最有效的逆集合

幾個問題

  1. 這是否意味着我必須喜歡設置了一個列表,單向一對多映射(雖然很明顯)?但有沒有解決辦法?
  2. 或者我必須調整我的域名,使其成爲雙向關係才能使用列表,尤其是當我有重複項目時。
+2

有人會想你在訂購時使用List,當你不需要時使用Set;根據需要建模的內容選擇您的模型......不考慮任何持久性解決方案。任何持久性解決方案應該能夠處理任何 – DataNucleus

+3

@DataNucleus:人們會認爲這一點。人們也會認爲Hibernate會有效地處理添加到單向映射。人有時會發現那些想法被誤導了! –

+0

@DataNucleas:即使我認爲是這樣,但在得到我上面提到的問題並且通過文檔之後,我發現它不止於此。 – shazinltc

回答

1

列表:允許在其中重複元素。

設置:所有元素應該是唯一的。

現在,刪除可能發生是因爲您正在覆蓋列表中的元素,所以當您修改UserAccount類型的持久實體時,它將刪除先前位於列表中的實體。

+0

不是,情況並非如此。請閱讀我列出的文件。它有刪除的原因。 – shazinltc

8

不久前我遇到過這個問題...在Hibernate中https://fedcsis.org/proceedings/2013/pliks/322.pdf

的一個性能反模式對於許多 協會總之:

我發現這篇文章

  • 袋語義 - >List/Collection + @OneToMany - >一個加入的元素:1刪除,N個插入,刪除一個元素:刪除1個,N個插入
  • 列表語義 - >List + @OneToMany + @IndexColumn/@OrderColumn - >一個加入的元素:1個插入,男更新,一個元素中刪除:1刪除,男更新
  • 語義集 - >Set + @OneToMany - >一個加入的元素:1個插入,一個元素中刪除:1刪除

對我來說:是的,這意味着您必須將您的List更改爲Set單向@OneToMany。所以我改變了我的模型匹配與Hibernate預期,導致了很多問題,因爲應用程序的視圖部分是依靠List大多...

在一方面Set是一個合乎邏輯的選擇對我來說,因爲有沒有重複,另一方面List更容易處理。

所以JPA/Hibernate迫使我改變模型對象,這不是第一次,當你使用@EmbededId你做了一些你可能不會以沒有JPA/Hibernate的相同方式做的事情。當你必須在所有應用程序中注意到HibernateProxy,尤其是在等於... else if(object instanceof HibernateProxy) { ...時,你注意到JPA/Hibernate persitence層在其他層中有點侵入性。

但是,當我直接使用JDBC我也用於改變模型或商業方法,以促進持久性... ... 圖層隔離可能是一個夢想或成本太多不能做到100%?

而且你可以訂購Set如果他們SortedSetTreeSet與註釋@OrderBy

帶來當某些代碼依賴於List並且不能更改(如JSF/PrimeFaces <dataTable><repeat>組件)存在問題 因此,您必須將Set更改爲List並返回Set,但如果您這樣做setNotifications(new HashSet<>(notificationList))您將有額外的查詢,因爲該集是由Hibernate管理的org.hibernate.collection.PersistentSet ...因此,我使用了addAll()removeAll() setter方法代替:

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) ; 
} 

注意你的@Entityequals()hashCode()方法...

另一個問題是,如果你想使用JPA和Hibernate作爲實現,你需要掌握JPA和Hibernate,因爲Set/List/Bag語義來自Hibernate而不是JPA(糾正我,如果我錯了)

一個規範是抽象的實現不依賴於一個特定的供應商。雖然大部分的JavaEE規範都成功了,但JPA失敗了,我放棄了獨立於休眠模式。

+0

謝謝!這篇文章真的更糟! –