Удаление дочернего элемента в отношении «один ко многим» создает исключение ObjectDeletedException - PullRequest
7 голосов
/ 12 мая 2011

Я просто не понимаю, почему Hibernate выбрасывает исключение, упомянутое в заголовке.Я, вероятно, не понимаю идею управления состоянием в Hibernate.

У меня следующая ситуация:

Отношение один-ко-многим между организацией и сотрудником

Organization.hmb.xml

<set name="employees" inverse="true" cascade="save-update">  
    <key column="organization_id"/>  
    <one-to-many class="Employee"/>  
</set>

Employee.hbm.xml

<many-to-one name="organization" class="Organization"  column="organization_id" />

Я использую стандартную архитектуру приложений Spring / Hibernate со службами и DAO, где DAO расширяют класс HibernateDaoSupport и используют службыкласса HibernateTemplate для управления сеансами.

Когда я пытаюсь удалить Employee в этом сценарии ...

Employee e=employeeService.read(1); 

//EDIT: Important! delete operation in EmployeeService is (@)transactional  
employeeService.delete(e); //this call just delegate execution to employeeDao.delete

EDIT : сначала я не упомянул, что удалениеОперация в слое Service является транзакционной, что представляется важной информацией (продолжайте читать)!

Hibernate throws ...

 ObjectDeletedException: deleted object would be re-saved by cascade...

Операция удаления в EmployeeService выглядит следующим образом ...

  @Transactional public void delete(Employee emp){  
    Employee e=employeeDao.read(emp.getId());  
    if(e==null)  
        throw NoSuchOrganizationException();  

    /*...several while-s to delete relations where Employee is  
    not owner of relation... */

    employeeDao.delete(e);  
}

Сценарии (они не связаны):
1. Когда я удаляю cascade = "save-update" из сопоставления отношений с Employee (s) в Organization.hbm.xml, все работает нормально .
2. Когда я удаляю аннотацию @ Transactional из метода delete, все работает нормально.
3. Когда я удаляю child (Employee) из родительского (Organization) списка детей , изатем выполните удаление, все работает нормально.

Вопрос :

Почему Hibernate вообще заботится о каскаде в родительском классе ?
Где находится точка исполнения, в которой он рассматривает каскад на объекте организации ?Почему он просто не может удалить Employee (Child) с помощью DELETE FROM ... и все.Кроме того, Сотрудник является владельцем отношений, и выполненные с ним операции должны сами управлять отношениями. В любом случае, когда он подумал вызвать какую-либо операцию на объекте Organization в упомянутом сценарии ?Я просто не понимаю

1 Ответ

11 голосов
/ 12 мая 2011

Что вам может не хватать, так это то, что Hibernate автоматически поддерживает состояние для сущностей, которые были загружены и существуют в Session, что означает, что оно сохранит любые изменения, внесенные в них независимо от того ли вы явно вызываете "update ()"method.

Учитывая ваш сценарий, если вы загрузили Organization и его набор employees и теперь пытаетесь удалить одного из этих сотрудников, Hibernate сообщает вам (выдав ObjectDeletedException), что онвосстановит удаленного сотрудника, когда сохранит Organization, потому что вы объявили, что сохранение или обновление должны каскадно передаваться от организации к сотрудникам в ее наборе.

Правильный способ справиться с этим - удалитьуказанный сотрудник из набора employees организации перед удалением, тем самым предотвращая каскадное повторное сохранение.

Редактировать (на основе обновленного вопроса):

Organization владеет собранием сотрудников.Каскад всегда следует за ассоциацией - вы объявили его на стороне Organization, поэтому изменения (сохранение / обновление) распространяются до уровня Employee.Поскольку конкретный экземпляр Employee является членом коллекции employees любого объекта Organization, который живет в сеансе, он будет сохранен (или повторно сохранен, поэтому создается исключение), когда сеанс сбрасывается / закрывается.

Тот факт, что сопоставление отображается со стороны Employee (например, столбец organization_id находится в таблице Employee) здесь не имеет значения;он мог быть сопоставлен через таблицу соединений с такими же результатами.Hibernate поддерживает «состояние» через сеанс, и когда вы пытаетесь удалить Employee, не удаляя его из Organization.employees, вы даете конфликтующие инструкции Hibernate (так как он все еще жив - и должен быть сохранен - ​​в одном месте, но удаленв другом), отсюда и исключение.

...