Hello,
We're seeing an odd problem when trying to do something quite simple. Delete a row.
The code that does this simply creates a new object, commits it, starts a new session, finds the object, deletes it. It dies with a constraint violation on an audit table, because hibernate is issuing an unnecessary update operation (and not incrementing the version either).
During the commit for the delete, a flush() is performed.
The DefaultFlushEntityEventListener.onFlushEntity does the following:
Code:
if ( isUpdateNecessary( event, mightBeDirty ) ) {
substitute = scheduleUpdate( event ) || substitute;
}
The question is, why does isUpdateNecessary return true, for an object that is to be deleted? The event status does say 'DELETED'.
The method itself looks like:
Code:
if ( mightBeDirty || status==Status.DELETED ) {
// compare to cached state (ignoring collections unless versioned)
dirtyCheck(event);
if ( isUpdateNecessary(event) ) {
return true;
}
else {
FieldInterceptor.clearDirty( event.getEntity() );
return false;
}
}
It seems odd to me that it'll return true if an update is necessary on a deleted object. If the object is to be deleted, why update?
Can anyone on the hibernate team confirm this to be a bug?
Hibernate version:
3.1 rc 2
Mapping documents:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.rbsfm.ice.marketdata.domain" default-access="field">
<class name="CurrentCurve" table="CURVE" dynamic-update="true" select-before-update="true">
<id name="id" column="id" unsaved-value="0" type="long">
<generator class="sequence">
<param name="sequence">CURVE_SEQ</param>
</generator>
</id>
<discriminator column="CURVE_TYPE" type="string" insert="false"/>
<version name="version" type="long"/>
<property name="name" type="string"/>
<property name="curveType" type="string" column="CURVE_TYPE"/>
<property name="location" type="string"/>
<property name="created" type="timestamp" column="CREATEDDATE" access="com.rbsfm.ice.common.persist.RbsDateTimeAccessor"/>
<property name="updated" type="timestamp" column="UPDATEDDATE" access="com.rbsfm.ice.common.persist.RbsDateTimeAccessor"/>
<property name="granularity" type="int"/>
<property name="daysDelayed" type="int" column="DAYS_DELAYED"/>
<property name="recoveryRate" type="double" column="RECOVERY_RATE"/>
<property name="updatedBy" type="string"/>
<property name="ticker" type="string"/>
<property name="derivationStrategyType" type="string" column="DERIVATION_STRATEGY"/>
<component name="referenceTenor" class="com.rbsfm.ice.marketdata.domain.Tenor">
<property name="symbolicDate" column="REFERENCE_TENOR"/>
</component>
<bag name="systems" table="CURVE_SYSTEM">
<key column="CURV_ID"/>
<many-to-many column="EXSY_ID" class="CurveSystem"/>
</bag>
<bag name="sectors" table="CURVE_GROUPING" lazy="false" fetch="select">
<key column="CURV_ID"/>
<many-to-many column="GROU_ID" class="Sector"/>
</bag>
<bag name="owners" table="CURVE_PERSON">
<key column="CURV_ID"/>
<many-to-many column="PERS_ID" class="com.rbsfm.ice.common.user.User"/>
</bag>
<bag name="points" batch-size="10" inverse="true" cascade="all,delete-orphan" lazy="false">
<key column="CURV_ID" not-null="true"/>
<one-to-many class="Point"/>
</bag>
<many-to-one name="holidaySchedule" column="HOSC_ID" lazy="false"/>
<many-to-one name="debtType" column="DETY_ID" lazy="false"/>
<many-to-one name="currency" column="CURR_ISO_CODE" lazy="false"/>
<many-to-one name="interpolation" column="ITYP_ID" lazy="false"/>
<many-to-one name="discountIndex" column="DIIN_ID" lazy="false"/>
<many-to-one name="restructuringCode" column="RECO_ID" lazy="false"/>
<many-to-one name="template" column="TMPL_ID" lazy="false"/>
<many-to-one name="referenceEntity" column="REEN_ID" lazy="false"/>
<many-to-one name="parent" column="CURV_ID" class="CurrentCurve"/>
<subclass name="CreditCurve" discriminator-value="C" dynamic-update="true">
</subclass>
<subclass name="IndexCurve" discriminator-value="I" dynamic-update="true">
<many-to-one name="creditIndex" column="CRIN_ID"/>
</subclass>
</class>
</hibernate-mapping>
Code between sessionFactory.openSession() and session.close():
Performed by spring interceptor
Full stack trace of any exception that occurs:
2005-11-17 10:44:06.591 ERROR [NDC=] [main] org.hibernate.event.def.AbstractFlushingEventListener Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: could not update: [com.rbsfm.ice.marketdata.domain.CreditCurve#10000100]
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:69)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2221)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2117)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2373)
at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:84)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:243)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:227)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:141)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:296)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:905)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:345)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:490)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:495)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:468)
at com.rbsfm.ice.common.persist.DBTestCase.commitAndStartNewSession(DBTestCase.java:56)
at com.rbsfm.ice.marketdata.CurveHomeDBTest.testCanDeleteCurve(CurveHomeDBTest.java:233)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at junit.framework.TestCase.runTest(TestCase.java:154)
at junit.framework.TestCase.runBare(TestCase.java:127)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:118)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:478)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:344)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: java.sql.SQLException: ORA-00001: unique constraint (ICED_MDD.AI$CURVE_I1) violated
ORA-06512: at "ICED_MDD.AU_CURVE", line 9
ORA-04088: error during execution of trigger 'ICED_MDD.AU_CURVE'
Name and version of the database you are using:
Oracle
The generated SQL (show_sql=true):
Hibernate: update CURVE set version=? where id=? and version=?
2005-11-17 10:47:52.929 DEBUG [NDC=] [main] org.hibernate.type.LongType binding '0' to parameter: 1
2005-11-17 10:47:52.929 DEBUG [NDC=] [main] org.hibernate.type.LongType binding '10000670' to parameter: 2
2005-11-17 10:47:52.929 DEBUG [NDC=] [main] org.hibernate.type.LongType binding '0' to parameter: 3