Hibernate version:
Hibernate Annotations 3.4.0.GA
Hibernate 3.3.1.GA
Hibernate EntityManager 3.4.0.GA
Mapping documents:
hibernate.cfg.xml
Code:
<hibernate-configuration>
<session-factory name="my-ds">
<event type="pre-delete">
<listener class="com.owner.services.hibernate.CustomCascadePreDeleteListener"/>
</event>
</session-factory>
</hibernate-configuration>
CustomCascadePreDeleteListener.java
Code:
public class CustomCascadePreDeleteListener implements PreDeleteEventListener {
public boolean onPreDelete(PreDeleteEvent event) {
Object entity = event.getEntity();
List<Object> relatedEntities = getRelatedEntities(entity);
for (Object relatedEntity : relatedEntities)
event.getSession().delete(relatedEntity);
return false;
}
}
Code between sessionFactory.openSession() and session.close():Code:
Object entity;
em.remove(entity);
Name and version of the database you are using:MySQL (but this is not relevant)
The problem:Imagine in this scenario that getRelatedEntities() returns a list of valid entities, then when calling session.delete, the action is put in the ActionQueue, but the action is never executed because it does:
in ActionQueue.java
Code:
private void executeActions(List list) throws HibernateException {
int size = list.size();
for ( int i = 0; i < size; i++ ) {
execute( ( Executable ) list.get( i ) );
}
list.clear();
session.getBatcher().executeBatch();
}
meaning that because we're already processing the deletions, and because it goes through the list using list.size(), then the new element added to the list is never executed.
A way of fixing it would be to do:
Code:
private void executeActions(List list) throws HibernateException {
while (!list.isEmpty())
execute( ( Executable ) list.get(0) );
session.getBatcher().executeBatch();
}
This would allow the adding of new cascaded delete events.
Also, this is not a full solution since we could imagine that in the delete listener you want to persist new entities, or update existing ones.
And, maybe you could do other type of listeners for other events that would require the same kind of feature.
This mean that the method in ActionQueue.java:
Code:
public void executeActions() throws HibernateException {
executeActions( insertions );
executeActions( updates );
executeActions( collectionRemovals );
executeActions( collectionUpdates );
executeActions( collectionCreations );
executeActions( deletions );
}
would also need a fix to be able to re-execute any type of actions when the executeActions(deletions) is finished.
A possible fix would be:
Code:
public void executeActions() throws HibernateException {
// Execute all actions until they are queued.
while (hasAnyQueuedActions()) {
executeActions( insertions );
executeActions( updates );
executeActions( collectionRemovals );
executeActions( collectionUpdates );
executeActions( collectionCreations );
executeActions( deletions );
}
}
Maybe I'm completely wrong in the way I'm using the listener, and maybe I should be put back on the right route, but it really seems to be a problem here.
Any help or comment is appreciated ;)
/Benoit