簡體   English   中英

JPA EntityManager.merge()嘗試將更新級聯到已刪除的實體

[英]JPA EntityManager.merge() attemps to cascade the update to deleted entities

我在EntityManager.merge()時遇到問題,其中合並級聯到已經從數據庫中刪除的其他實體。 說我有以下實體:

@Entity
public class Parent {
  @OneToMany(cascade = CascadeType.ALL, orphanremoval = true, mappedBy = "parent")
  private List<Child> children;

  public void clearChildren() { children.clear(); }
  public void createChildren(Template template) { ... }
}

@Entity
public class Child {
  @ManyToOne
  @JoinColumn(name = "parentId")
  private Parent parent;
}

發生問題的情況如下:

  1. 用戶創建一個新的Parent實例,並通過調用createChildren()方法根據所選模板創建新的Child實例。 該模板定義了所創建子代的數量和屬性。
  2. 用戶保存父級,從而將持久性級聯到子級。
  3. 用戶注意到所使用的模板是錯誤的。 他更改了模板並保存,從而導致刪除了舊的孩子並創建了新的孩子。

通常,刪除舊的子項將由orphanRemoval屬性自動處理,但是Child實體具有多列唯一索引,並且基於新模板創建的某些新子項在索引的所有列中都可以具有相同的值像一些原來的孩子一樣。 將更改刷新到數據庫后,JPA會在刪除之前( 或至少是Hibernate這樣做 )執行插入和更新操作 ,並且會發生約束沖突。 Oracle的延遲約束可以解決此問題,但我們還支持MS SQL,而AFAIK不支持MS SQL(如果我錯了,請糾正我)。

因此,為了解決此問題,我手動刪除了舊的子項,刷新更改,創建新的子項並保存我的更改。 下面的人工代碼段顯示了當前情況的基本部分。 由於我們框架的工作方式, 傳遞給該方法的實體始終處於分離狀態 (恐怕是問題的一部分)。

public void createNewChildren(Parent parent, Template template) {
  for (Child child : parent.getChildren()) {
    // Have to run a find since the entities are detached
    entityManager.remove(entityManager.find(Child.class, child.getId()));
  }
  entityManager.flush();
  parent.clearChildren();
  parent.createChildren(template);
  entityManager.merge(parent); // EntityNotFoundException is thrown
}

最后一行在EntityManager嘗試加載舊子項並將其合並時引發異常,但由於已被刪除而失敗。 問題是, 為什么它首先嘗試加載它們? 更重要的是, 我該如何預防呢? 我想到的唯一可能導致此問題的是過時的緩存問題。 我無法刷新父級,因為它可能包含其他未保存的更改,而這些更改將丟失(加上已分離)。 在刪除每個孩子之前,我嘗試將其父引用顯式設置為null,並在刪除它們之后嘗試從第二級緩存中逐出這些老孩子。 都沒有幫助。 我們沒有以任何方式修改JPA緩存設置。

我們正在使用Hibernate 4.3.5。

更新:

實際上,我們也正在清除父母的孩子,這本來可能有點模棱兩可,所以我更新了代碼片段以使其清晰。

請嘗試從父級中刪除子級,然后再刪除它們,這樣MERGE就無法級聯到它們,因為它們不在父級的集合中。

for (Child child : parent.getChildren()) {
    // Have to run a find since the entities are detached
    Child c = entityManager.find(Child.class, child.getId());
    parent.getChildren().remove(c); // ensure that the child is actually removed
    entityManager.remove(c);
}

UPDATE

我仍然認為操作順序是造成問題的原因,請嘗試是否可行

public void createNewChildren(Parent parent, Template template) {
  for (Child child : parent.getChildren()) {
    // Have to run a find since the entities are detached
    Child c = entityManager.find(Child.class, child.getId());
    parent.getChildren().remove(c); // ensure that the child is actually removed
    c.setParent(null);
    entityManager.remove(c);
  }
  parent.createChildren(template);
  entityManager.merge(parent);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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