Hibernate «удаленная сущность передана для сохранения» проблема с Cascade.DELETE_ORPHAN и отношения родитель / потомок - PullRequest
2 голосов
/ 02 августа 2011

У меня есть следующее отображение собственной таблицы:

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 указывает на идентификатор родительского узла.

Я реализовал некоторые базовые операции с деревом:

  1. Массовое удаление: удалить выбранный узел + все его дочерние элементы
  2. Одиночное удаление: удалить только выбранный узел, повторно-прикрепить все его потомки к его родителю
  3. ect ...

Для выполнения операции 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 ]

1 голос
/ 03 августа 2011

Наконец, я выяснил причину такого поведения, посмотрев в исходный код:

Допустим, у нас есть отец N0, ребенок N1, у которого есть собственный ребенок N2.

Операция:

  1. Удалить N2 из списка детей N1
  2. Добавить N2 в список детей N0
  3. Удалить N1 из списка детей N0

Во время сброса () вызывается следующий фрагмент кода:

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 содержит N2 , а коллекция currentElements пустой. Согласно кодексу, Hibernate считает, что все старые дети теперь являются сиротами, он не проверяет, принадлежат ли они новому родителю.

Решения будут, как предложил Райан:

  1. Дублируйте старых детей как временные объекты и прикрепляйте их к отцу
  2. Удаление каскада DELETE_ORPHAN и удаление вручную сирот
0 голосов
/ 02 августа 2011

«Удалить сироту» означает только то, что объект, удаленный из коллекции, будет удален.При этом не учитывается, будет ли объект добавлен в другую коллекцию.Флаг на самом деле связан с коллекцией, а не с отдельными объектами.С этим согласен и ваш опыт, а также описание javax.persistence.OneToMany.orphanRemoval , которое, кстати, заменило CascadeType.DELETE_ORPHAN.Последний сейчас устарел.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...