使用Cascade.DELETE_ORPHAN和父/子关系将Hibernate“删除实体传递给持久化”问题

时间:2011-08-01 22:08:34

标签: java hibernate

我有以下自表映射:

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. 等...
  4. 实施操作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真的不重要。

    关于这个问题的任何线索?

2 个答案:

答案 0 :(得分:1)

最后,我通过查看源代码找到了这种行为的原因:

假设我们有一个父亲N0,一个孩子N1,它有自己的孩子N2。

操作是:

  1. 从N1子列表中删除N2
  2. 将N2添加到N0子列表
  3. 从N0子列表中删除N1
  4. 在flush()期间,调用以下代码:

    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
        ...
        ...
    

    实际上,当为节点 N1 输入方法getOrphans()时, oldElements 集合包含 N2 currentElements 收集是空的。根据代码,Hibernate认为现在所有的孩子都是孤儿,它不会检查他们是否属于新的父母。

    解决方案将如Ryan所说:

    1. 将旧孩子复制为临时对象并将其附加到父亲
    2. 删除DELETE_ORPHAN级联并手动删除孤儿

答案 1 :(得分:0)

“删除孤立”仅表示从集合中删除的对象被删除。它没有考虑对象是否被添加到另一个集合。该标志实际上与集合相关联,而不是与单个对象相关联。您的经验与此一致,javax.persistence.OneToMany.orphanRemoval的描述也是如此,顺便说一下,它取代了CascadeType.DELETE_ORPHAN。后者现已弃用。