-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 
Author Message
 Post subject: Possible bug with deleting versioned object
PostPosted: Thu Nov 17, 2005 6:54 am 
Newbie

Joined: Mon Apr 19, 2004 3:24 pm
Posts: 9
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


Top
 Profile  
 
 Post subject: More information
PostPosted: Thu Nov 17, 2005 10:10 am 
Newbie

Joined: Mon Apr 19, 2004 3:24 pm
Posts: 9
If we remove the <version/> element from the mapping, and update the version manually, the problem goes away.

This of course is not what we want (if someone modifies a curve via reachability from some other object - we want the curve version to be incremented).

Further debugging shows that Hibernate thinks that three properties on the object are 'modified'. These three are collections. One is the points collection, one owners and the other is systems. Hibernate seems to think that they are 'different' because null != an empty collection. So, it thinks it needs to update the object.

However; later on - it doesn't increment the version number - because it knows the object is to be deleted. Thus - a problem.

I hope this information helps in determining the problem, and it's solution.
Please request more information if required.


Top
 Profile  
 
 Post subject: More info
PostPosted: Thu Nov 17, 2005 11:08 am 
Newbie

Joined: Mon Apr 19, 2004 3:24 pm
Posts: 9
Further information (but no further to a solution):

During the flush, hibernate thinks that a number of properties are dirty. This is because they are null, as opposed to an empty collection (PersistentBag).

The above behavior appears to be due to the deletedState constructed by the DefaultDeleteEventListener.deleteEntity method. The deep copy only copies those properties that are 'updatable'.

Hmmm....

Regards,
Neil Clayton


Top
 Profile  
 
 Post subject: A solution - but I'm not sure if it's correct
PostPosted: Thu Nov 17, 2005 12:32 pm 
Newbie

Joined: Mon Apr 19, 2004 3:24 pm
Posts: 9
The following patch solves our problem.

Essentially, we see that the checkable arg is passed in, but never used. In the case of properties that are not updateable, checkable contains an empty boolean array. Other properties contain true/false depending on te values retrieved from the persister.

Because of this, dirty checks are still performed - yet the deletedState does take these into account. Thus the problem.

If I change CollectionType.isDirty to read:

Code:
   public boolean isDirty(Object old, Object current, boolean[] checkable, SessionImplementor session)
         throws HibernateException {
        if(checkable.length == 0) {
            // Assume not checkable
            return false;
        }
      return isDirty(old, current, session);
   }


Then the delete on a versioned object with collections works.
Can a knowledgable hibernate person confirm that this is a valid patch to apply?

Regards,
Neil Clayton


Top
 Profile  
 
 Post subject: Same problem
PostPosted: Wed Jan 04, 2006 5:54 pm 
Newbie

Joined: Tue Dec 13, 2005 10:00 pm
Posts: 7
I'm having the same problem with versioned entities on a delete. They seem to be updated prior to being deleted, causing a StaleObject Exception.

We're working fine now with versioning disabled. Haven't made it to 3.1 yet to attempt this patch. Has any hibernate person looked at this patch?

Thanks


Top
 Profile  
 
 Post subject: Deletes with unnecessary updates
PostPosted: Mon Jan 30, 2006 5:03 pm 
Newbie

Joined: Sat Nov 05, 2005 3:10 pm
Posts: 2
We are having the problem as well but have not attempted the patch yet. We are still using 3.0.5


Top
 Profile  
 
 Post subject: Logged issue HHH-1564
PostPosted: Mon Mar 13, 2006 11:17 am 
Newbie

Joined: Mon Apr 19, 2004 3:24 pm
Posts: 9
I've logged this as a bug, ref: HHH-1564


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 17, 2006 7:48 pm 
Hibernate Team
Hibernate Team

Joined: Mon Jan 23, 2006 9:18 pm
Posts: 14
I noticed that the proposed change to CollectionType.isdirty(Object old, Object current, boolean[] checkable, SessionImplementor session), as it stands, applies only to Hibernate 3.1. The method will not be called in Hibernate 3.0.5 without making other changes because the superclass does not have this method.

I have also verified that applying the fix to hibernate 3.1 seems to work, but it causes unit test org.hibernate.test.legacy.FooBarTest.testVersionedCollections to fail as follows:

Testcase: testVersionedCollections took 0.146 sec
FAILED
versioned collection before
junit.framework.AssertionFailedError: versioned collection before
at org.hibernate.test.legacy.FooBarTest.testVersionedCollections(FooBarTest.java:3042)
at org.hibernate.test.TestCase.runTest(TestCase.java:150)

This failure does not occur using the 3.1 code without this fix.

It seems strange to update a versioned entity without incrementing the version number. It also does not make sense to increment the version number for a delete, since it is the n-th version being deleted, not the (n+1)-th version. I have also verified that updates to an entity's collection are not cascaded if that entity is deleted in the same transaction as the update, so I'm not sure how a collection could ever dirty an entity on a delete requiring an update.

A possible workaround for this issue is to modify the trigger. Since triggers have access to the new and old values for the row, it could be changed to write to the audit table only when the new and old values are different. This may or may not be acceptable for a particular situation but maybe this could work in your case.

This comment is also entered in the bug report.

Gail Badner


Top
 Profile  
 
 Post subject:
PostPosted: Fri Dec 21, 2007 2:28 pm 
Newbie

Joined: Thu Sep 27, 2007 10:48 am
Posts: 5
Did you manage to resolve the problem yet guys ?

_________________
School is a drill for the battle of life. If you fail in the drill you will fail in the battle.
Karl G. Maeser
Costa Rica hotel


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.