I just don't understand why Hibernate throws exception mentioned in title. I probably don't understand state management idea behind Hibernate.
I have following situation:
One-to-many relation between Organization and Employee
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" />
I use standard Spring/Hibernate app architecture with Services and DAOs, where DAOs extend HibernateDaoSupport class and use services of HibernateTemplate class for Session management.
When I try to delete Employee in this scenario...
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: I did not mention at first that delete operation in Service layer is transactional which seems to be important info (keep reading)!
Hibernate throws...
ObjectDeletedException: deleted object would be re-saved by cascade...
Delete operation in EmployeeService looks like...
@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);
}
Scenarios (they are not related):
1. When i remove cascade="save-update" from relation mapping to Employee(s) in Organization.hbm.xml, everything works fine.
2. When i remove @Transactional annotation from delete method everything works fine.
3. When i delete child(Employee) from parent(Organization) list of children, and then execute delete, everything works fine.
Question:
Why Hibernate cares at all about cascade in parent class?
Where is the point in execution at which he considers cascade on Organization object?
Why he just can't delete Employee(Child) with DELETE FROM... and that's it. Besides, Employee is the owner of relationship and operations executed on him should manage relation itself. When he thought to call any operation on Organization object in mentioned scenario anyway? I just don't get it.
What you may be missing is that Hibernate automatically maintains state for entities that have been loaded and exist in Session, meaning it will persist any changes made to them irregardless of whether you explicitly invoke "update()" method.
Considering your scenario, if you have loaded Organization
and its employees
set and are now trying to delete one of those employees, Hibernate is telling you (by throwing ObjectDeletedException
) that it will re-save deleted employee when it will save Organization
because you've declared that saving or updating should be cascaded from organization down to employees in its set.
The proper way to handle this would be to remove said employee from organization's employees
set before deleting it thus preventing cascaded re-save.
Edit (based on updated question):
Organization
owns collection of employees. Cascade always follows the association - you've declared it on Organization
side, so changes (save / update) are propagated down to Employee
level. As long as particular Employee
instance is a member of employees
collection of any Organization
entity that lives in session, it will be saved (or re-saved, so Exception is thrown) when session is flushed / closed.
The fact that association is mapped from Employee
side (e.g. organization_id
column resides in Employee
table) is irrelevant here; it could have been mapped via join table with the same results. Hibernate maintains "state" via session and when you're trying to delete Employee
without removing it from Organization.employees
you're giving Hibernate conflicting instructions (since it's still alive - and has to be saved - in one place but is deleted in the other), hence the exception.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With