[hibernate-issues] [Hibernate-JIRA] Commented: (HHH-1196) Avoid unnecessary updates when cascading the deletes
Frederic Leitenberger (JIRA)
noreply at atlassian.com
Fri Sep 8 06:39:25 EDT 2006
[ http://opensource.atlassian.com/projects/hibernate/browse/HHH-1196?page=comments#action_24370 ]
Frederic Leitenberger commented on HHH-1196:
--------------------------------------------
I had the same problem with a much more important problem than an additional sql-statement.
We implemented our own advanced property- and state-transition-validation using hibernate-interceptors.
Property-Validation is done in onFlushDirty and onSave.
State-Transition-Validation ist done in onPreSave, onPreUpdate and onPreDelete.
Short description of the problem:
- save entity and attach childs to it
- flush/commit
- delete and/or detach all childs and delete the entity
- flush/commit
During the last flush/commit the entity is deleted and afterwards updated/flushed before final deletion ...
This produces problems during validation since the current state during flush is almost empty and causes validation to fail.
The state-transition validation fails too, because a delete causes the object to change its state into a wrong state first before finally deleted.
Here are some code nipps and stacks:
All in same session with same entities.
Transaction tx = session.beginTransaction();
E1 entity1 = new E1();
session.save(entity1);
at de.iccs.api.validation.HibernateInterceptor.onSave(HibernateInterceptor.java:31)
at org.hibernate.event.def.AbstractSaveEventListener.substituteValuesIfNecessary(AbstractSaveEventListener.java:348)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:247)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:167)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:114)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:186)
at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:33)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:175)
at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:27)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:530)
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:518)
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:514)
tx.commit(); // insert for entity1 - ok
at de.iccs.api.validation.HibernateInterceptor.onPreInsert(HibernateInterceptor.java:148)
at org.hibernate.action.EntityInsertAction.preInsert(EntityInsertAction.java:138)
at org.hibernate.action.EntityInsertAction.execute(EntityInsertAction.java:44)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:248)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:232)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:139)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:297)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:985)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:333)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
(...)
// new Transaction ...
Transaction tx = session.beginTransaction();
E2 entity2 = new E2();
// ManyToOne [E2.e1_id -> E1.id]
entity2.setE1(entity1);
entity1.getE2s().add(entity2); // no effective change to entity1 (relation owned by E2)
tx.commit(); // flush for entity1 ?? why? (maybe version inc) ok
at de.iccs.api.validation.HibernateInterceptor.onFlushDirty(HibernateInterceptor.java:25)
at org.hibernate.event.def.DefaultFlushEntityEventListener.invokeInterceptor(DefaultFlushEntityEventListener.java:318)
at org.hibernate.event.def.DefaultFlushEntityEventListener.handleInterception(DefaultFlushEntityEventListener.java:294)
at org.hibernate.event.def.DefaultFlushEntityEventListener.scheduleUpdate(DefaultFlushEntityEventListener.java:234)
at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:114)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:195)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:76)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:985)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:333)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at de.iccs.hibernate.SessionContainer.commitTransaction(SessionContainer.java:63)
(...)
// new Transaction ...
Transaction tx = session.beginTransaction();
// no change to entity1
session.createQuery("from E3 where ...").uniqueResult(); // flush for entity1 ??? why?
at de.iccs.api.validation.HibernateInterceptor.onFlushDirty(HibernateInterceptor.java:25)
at org.hibernate.event.def.DefaultFlushEntityEventListener.invokeInterceptor(DefaultFlushEntityEventListener.java:318)
at org.hibernate.event.def.DefaultFlushEntityEventListener.handleInterception(DefaultFlushEntityEventListener.java:294)
at org.hibernate.event.def.DefaultFlushEntityEventListener.scheduleUpdate(DefaultFlushEntityEventListener.java:234)
at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:114)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:195)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:76)
at org.hibernate.event.def.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:35)
at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:954)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1099)
at org.hibernate.impl.QueryImpl.list(QueryImpl.java:79)
at org.hibernate.impl.AbstractQueryImpl.uniqueResult(AbstractQueryImpl.java:749)
(...)
// no change to entity1
session.createQuery("from E3 where ...").uniqueResult(); // flush for entity1 ??? why?
at de.iccs.api.validation.HibernateInterceptor.onFlushDirty(HibernateInterceptor.java:25)
at org.hibernate.event.def.DefaultFlushEntityEventListener.invokeInterceptor(DefaultFlushEntityEventListener.java:318)
at org.hibernate.event.def.DefaultFlushEntityEventListener.handleInterception(DefaultFlushEntityEventListener.java:294)
at org.hibernate.event.def.DefaultFlushEntityEventListener.scheduleUpdate(DefaultFlushEntityEventListener.java:234)
at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:114)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:195)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:76)
at org.hibernate.event.def.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:35)
at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:954)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1099)
at org.hibernate.impl.QueryImpl.list(QueryImpl.java:79)
at org.hibernate.impl.AbstractQueryImpl.uniqueResult(AbstractQueryImpl.java:749)
(...)
for(E2 e2 : entity1.getE2s()) {
if (ex) {
e2.setE1(null); // detach from entity1
session.update(e2);
} else {
session.delete(e2);
}
}
entity1.getE2s().clear();
session.delete(entity1);
at de.iccs.api.validation.HibernateInterceptor.onDelete(HibernateInterceptor.java:38)
at org.hibernate.event.def.DefaultDeleteEventListener.deleteEntity(DefaultDeleteEventListener.java:157)
at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:110)
at org.hibernate.impl.SessionImpl.fireDelete(SessionImpl.java:761)
at org.hibernate.impl.SessionImpl.delete(SessionImpl.java:739)
(...)
tx.commit(); // flush for entity1 ??? why?
at de.iccs.api.validation.HibernateInterceptor.onFlushDirty(HibernateInterceptor.java:25)
at org.hibernate.event.def.DefaultFlushEntityEventListener.invokeInterceptor(DefaultFlushEntityEventListener.java:318)
at org.hibernate.event.def.DefaultFlushEntityEventListener.handleInterception(DefaultFlushEntityEventListener.java:294)
at org.hibernate.event.def.DefaultFlushEntityEventListener.scheduleUpdate(DefaultFlushEntityEventListener.java:234)
at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:114)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:195)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:76)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:985)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:333)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
(...)
// update for entity1 ??? why?
at de.iccs.api.validation.HibernateInterceptor.onPreUpdate(HibernateInterceptor.java:169)
at org.hibernate.action.EntityUpdateAction.preUpdate(EntityUpdateAction.java:216)
at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:64)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:248)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:232)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:140)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:297)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:985)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:333)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
(...)
// delete for entity1 --> that's what i expected
at de.iccs.api.validation.HibernateInterceptor.onPreDelete(HibernateInterceptor.java:192)
at org.hibernate.action.EntityDeleteAction.preDelete(EntityDeleteAction.java:108)
at org.hibernate.action.EntityDeleteAction.execute(EntityDeleteAction.java:47)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:248)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:232)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:144)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:297)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:985)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:333)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
(...)
I have an unlucky workaround for now.
I set the version onDelete to null and skip the validation when the version is null (defaults to -1).
Please review/reopen this issue and fix it if possible!!
> Avoid unnecessary updates when cascading the deletes
> ----------------------------------------------------
>
> Key: HHH-1196
> URL: http://opensource.atlassian.com/projects/hibernate/browse/HHH-1196
> Project: Hibernate3
> Type: Improvement
> Components: core
> Versions: 3.1 rc3
> Reporter: tHE DPR
>
>
> Consider this mapping:
> <class name="Category" table="category">
> <id name="id"><generator class="native"/></id>
> <version name="version"/>
> <property name="description" not-null="true" column="description"/>
> <many-to-one name="parent" column="parent_id" cascade="persist"/>
> <set name="children" inverse="true" cascade="all">
> <key column="parent_id"/>
> <one-to-many class="Category"/>
> </set>
> </class>
> Now suppose we have a root category with some children
> and try to delete one of them:
> Category root = ... ;
> Category child = root.children().iterator().next();
> root.remove(child);
> session.delete(child);
> session.flush();
> Hibernate issues these SQL statements:
> - an update for the root entity incrementing the version
> that's ok because the set of children has changed
> - an unnecessary update for the child entity to be deleted
> this update changes no fields (even version) and IMO is unnecessary
> - the deletes for the chilren of deleted child
> That unnecessary update is executed even when deleted child has no children.
> Its children collection is changed from empty set to null
> and that is detected as property change, hence the update.
> A better strategy would be not to update entities that are going to be
> deleted during the flush.
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
http://opensource.atlassian.com/projects/hibernate/secure/Administrators.jspa
-
For more information on JIRA, see:
http://www.atlassian.com/software/jira
More information about the hibernate-issues
mailing list