[
http://opensource.atlassian.com/projects/hibernate/browse/HHH-1196?page=c...
]
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....
-
For more information on JIRA, see:
http://www.atlassian.com/software/jira