[英]How to avoid duplicates with JPA cascades?
我有一個多對一關系子實體父實體:
@Entity class Parent {
// ...
@ManyToOne((cascade = {CascadeType.ALL})
private Child child;
// ...
}
Child有一個獨特的字段:
@Entity class Child {
// ...
@Column(unique = true)
private String name;
// ...
}
當我需要一個新的Child 時,我首先詢問ChildDAO :
Child child = childDao.findByName(name);
if(child == null) {
child = new Child(name);
}
Parent parent = new Parent();
parent.setChild(child);
問題是,如果我喜歡上面兩次(與Child同名),並且最后只保留Parent ,我會得到一個約束異常。 這似乎很正常,因為最初數據庫中沒有指定名稱的子項。
問題是,我不確定避免這種情況的最佳方法是什么。
您正在使用new Child()
創建兩個 Child 的非持久性實例,然后將它們放入兩個不同的 parent。 當您持久化 Parent 對象時,兩個新的 Child 實例中的每一個都將通過級聯持久化/插入,每個實例都有不同的@Id
。 名稱上的唯一約束隨后中斷。 如果您正在執行 CascadeType.ALL,那么每次執行new Child()
您可能會得到一個單獨的持久對象。
如果您真的希望將兩個 Child 實例視為具有相同 ID 的單個持久對象,則需要單獨持久化它以與持久性上下文/會話相關聯。 隨后對childDao.findByName
調用將刷新插入並返回您剛剛創建的新 Child,因此您不會執行new Child()
兩次。
你得到這個是因為你試圖持久化一個已經存在的對象(相同的 ID)。 您的級聯可能不是持久的,它是 MERGE/UPDATE/REMOVE/DETACH。 避免這種情況的最佳方法是正確設置級聯或手動管理級聯。 基本上來說,級聯持續是通常的罪魁禍首。
你可能想要這樣的東西:
//SomeService--I assume you create ID's on persist
saveChild(Parent parent, Child child)
{
//Adding manually (using your cascade ALL)
if(child.getId() == null) //I don't exist persist
persistence.persist(child);
else
persistence.merge(child); //I'm already in the DB, don't recreate me
parent.setChild(child);
saveParent(parent); //using a similar "don't duplicate me" approach.
}
如果讓框架管理級聯,級聯可能會非常令人沮喪,因為如果它正在緩存(特別是多對一關系中的集合),您偶爾會錯過更新。 如果您沒有明確保存父級並允許框架處理級聯。 一般來說,我允許在一段關系中來自我父母的 Cascade.ALL 和來自我孩子的 DELETE/PERSIST 的所有級聯。 我是 Eclipselink 用戶,所以我在 Hibernate/other 方面的經驗有限,我無法談論緩存。 * 真的,想想看——如果你在拯救一個孩子,你真的想拯救所有與它相關的物體嗎? 另外,如果你要添加一個孩子,你不應該通知父母嗎? *
在您的情況下,我只會在我的 Dao/Service 中使用“saveParent”和“saveChild”方法,以確保緩存正確且數據庫正確以避免頭痛。 手動管理意味着您將擁有絕對的控制權,而不必依賴級聯來完成繁重的工作。 在一個方向上,它們很棒,一旦它們來到“上游”,你就會有一些意想不到的行為。
如果您創建兩個對象,並讓 JPA 自動持久化它們,那么您將在數據庫中獲得兩行。 因此,您有兩個選擇:不要創建兩個對象,或者不要讓 JPA 自動持久化它們。
為了避免創建兩個對象,您必須安排您的代碼,以便如果兩個父項嘗試創建具有相同名稱的子項,他們將獲得相同的實際實例。 您可能需要一個WeakHashMap (范圍為當前請求,可能通過局部變量引用),以名稱為鍵,父母可以在其中查找新孩子的名稱以查看孩子是否已經存在。 如果沒有,他們可以創建對象並將其放入地圖中。
為避免 JPA 自動持久化對象,請刪除級聯並在創建后立即使用persist手動將對象添加到上下文中。
由於持久化上下文基本上是一個附加到數據庫的經過精心設計的 WeakHashMap,因此歸根結底,這些方法非常相似。
您正在設置一個子對象,然后如果堅持父對象,您將在數據庫中存儲一個指向不存在的子對象的寄存器。
當您創建一個新對象時,它必須由 entityManager 以相同的方式管理,當您使用 DAO“查找”一個對象時,它必須從 DB 獲取寄存器並將該對象放入 entityManager 上下文中。
嘗試先持久化或合並子對象。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.