2011-08-01 72 views
2

我有以下的自表映射:休眠「刪除被persist實體」的問題與Cascade.DELETE_ORPHAN和父/子關係

public class Node implements { 

@Id 
@GeneratedValue(strategy = GenerationType.AUTO) 
private Long id; 

    ... 

@ManyToOne(fetch = FetchType.LAZY) 
@JoinColumn(name = "IDFATHER", referencedColumnName = "ID") 
private Node father; 

@OneToMany(mappedBy = "father", fetch = FetchType.EAGER) 
@Fetch(FetchMode.JOIN) 
@Cascade(value = 
{ 
     CascadeType.ALL, 
     CascadeType.DELETE_ORPHAN 
}) 
private List<Node> children; 

基本上它是使用同一個表經典的父/子節點樹,IDFATHER列指向父節點的ID。

我已經實現在樹上的一些基本操作:

  1. 批量刪除:刪除選定的節點+所有子女
  2. 單刪除:只刪除選定的節點,其所有的孩子重新重視其父
  3. 等...

要實現操作2)單刪除:

// father of the node to be deleted 
Node father = deletedNode.getFather(); 

if (deletedNode.getChildCount() != 0) 
{ 
    List<eNode> tempChildren = new ArrayList<Node>(); 

    // put all children of deleted node in a temp list because the 
    // new FOR loop doesn't allow concurrent modification while looping 
    for (Node child : deletedNode.getChildren()) 
    { 
      tempChildren.add(child); 
    } 

    for (Node child : tempChildren) 
    { 
     // re-attach first all the children to the father 
     father.getChildren().add(child); 
     child.setFather(father); 

     // remove all the children from the deleted node list 
     deletedNode.getChildren().remove(child); 

     // remove the deleted node from the father children' list 
     father.getChildren().remove(deletedNode); 
    } 
} 
this.nodeDAO.flush(); 

我得到異常

javax.persistence.EntityNotFoundException:刪除的實體傳遞給 堅持

據我瞭解,根據官方的文檔,當你刪除一個實體, Cascade.ALL,刪除級聯到它的子級。然而,在這種特殊情況下,所有的孩子都被重新綁在父親身上,所以他們不應該被刪除...

當我刪除Cascade.DELETE_ORPHAN,它的工作原理。從邏輯上講,通過將孩子重新附加到父親身上,他們不再是孤兒,所以Cascade.DELETE_ORPHAN應該不是很重要。

在這個問題上的任何線索?

回答

0

「刪除孤立」僅表示從集合中刪除的對象被刪除。它不考慮對象是否被添加到另一個集合中。該標誌實際上與集合相關聯,而不是與單個對象關聯。您的經驗與此相同,正如javax.persistence.OneToMany.orphanRemoval的說明,順便說一下,它已經取代了CascadeType.DELETE_ORPHAN。後者現在已被棄用。

+0

謝謝你的回覆瑞恩。所以,如果我明白DELETE_ORPHAN的語義沒有考慮到一個孩子有一個新的父親的可能性... – doanduyhai

+0

不是我曾經注意過。回想起來,這看起來很奇特,但這並不是你經常會遇到的情況。我想你的選擇是要麼自己管理刪除,要麼創建新的節點以移植到樹中,而不是重複使用舊節點。 –

1

最後我發現這種行爲的原因,通過查看源代碼:

比方說,我們有父親N0,孩子N1有自己的孩子N2。

操作是:

  1. 從N1孩子列表中刪除N2
  2. 添加N2至N0兒童名單
  3. 從N0兒童名單

刪除N1在沖洗()下面這段代碼被稱爲:

org.hibernate.collection.PersistentBag 

public Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException { 
    List sn = (List) snapshot; 
    return getOrphans(sn, bag, entityName, getSession()); 
} 

org.hibernate.collection.AbstractPersistentCollection 

protected static Collection getOrphans(
     Collection oldElements, 
     Collection currentElements, 
     String entityName, 
     SessionImplementor session) 
throws HibernateException { 

    // short-circuit(s) 
    if (currentElements.size()==0) return oldElements; // no new elements, the old list contains only Orphans 
    if (oldElements.size()==0) return oldElements; // no old elements, so no Orphans neither 
    ... 
    ... 

的確,對於節點進入方法getOrphans()時N1,所述oldElements集合包含N2currentElements集合爲空。根據代碼,Hibernate認爲現在所有的孩子都是孤兒,它不會檢查他們是否屬於新父母。

的解決方案將是,萊恩建議:

  1. 複製舊的孩子短暫的對象,並將它們連接到父親
  2. 取出DELETE_ORPHAN級聯和手動刪除孤兒