簡體   English   中英

如何避免與 JPA 級聯重復?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM