Version incorrectly incremented for unchanged persistent entity that is parent of a one to many relationship

Description

Given a persisted parent entity and a single persisted child entity involved in a one to many relationship where children are defined in parent HBM file to include merge and persist cascade action, we find that loading the parent and child in a transaction followed by a merge of the parent (no updates performed on either child or parent), and then a transaction commit will increment the version of the parent by 1.

In attached zip I've isolated the problem in a new test testNoExtraUpdatesOnPersistentMergeVersionedWithCollection(), displayed below, added to org/hibernate/test/ops/MergeTest.java. It fails when parent is checked for updates following transaction commit. While the behavior was observed originally against 3.2.4.sp1, I can also reproduce in 3.2.6. In our system this issue causes org.hibernate.StaleObjectStateException exceptions when load is applied.

I believe I've tracked down the origin of the failure. The merge(persistentParent) call results in child collection of parent being cleared, and consequently marked dirty. This happens when org.hibernate.type.CollectionType method replaceElements() is invoked as part of processing cascade action for merge on parent. It seems dirty flag should be reset after call to this method by CollectionType.replace(), performed when original==target. I see a similar effort was applied to replaceElements() but unfortunately this has no impact for this particular test scenario. I've applied a change to CollectionType.replace() to reset dirty flag if 'original' object was not dirty upon method entry. This resolves the issue for both 3.2.4.sp1 and 3.2.6 and I don't see any adverse impact to execution of Hibernate tests against HSQL. The updated CollectionType class is supplied in attached zip (deltas displayed below).

Definition of children for parent in HBM definition OptLockEntity of org/hibernate/test/ops:
<set name="children"
inverse="true"
cascade="persist,merge,save-update,evict,delete">
<key column="parent"/>
<one-to-many class="VersionedEntity"/>
</set>

Index: test/org/hibernate/test/ops/MergeTest.java
===================================================================
— test/org/hibernate/test/ops/MergeTest.java (revision 14669)
+++ test/org/hibernate/test/ops/MergeTest.java (working copy)
@@ -248,6 +248,56 @@
cleanup();
}

+ public void testNoExtraUpdatesOnPersistentMergeVersionedWithCollection() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ VersionedEntity parent = new VersionedEntity( "parent", "parent" );
+ VersionedEntity child = new VersionedEntity( "child", "child" );
+ parent.getChildren().add( child );
+ child.setParent( parent );
+ s.persist( parent );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ // parent is now detached, but we have made no changes. so attempt to merge it
+ // into this new session; this should cause no updates...
+ s = openSession();
+ s.beginTransaction();
+ // load parent so that merge will follow entityIsPersistent path
+ VersionedEntity persistentParent = ( VersionedEntity ) s.get( VersionedEntity.class, parent.getId() );
+ // load children
+ VersionedEntity persistentChild = ( VersionedEntity ) persistentParent.getChildren().iterator().next();
+ VersionedEntity mergedParent = ( VersionedEntity ) s.merge( persistentParent ); // <-- This merge leads to failure
+ s.getTransaction().commit();
+ s.close();
+
+ assertUpdateCount( 0 );
+ assertInsertCount( 0 );
+ assertEquals( "unexpected parent version increment", parent.getVersion(), mergedParent.getVersion() );
+ VersionedEntity mergedChild = ( VersionedEntity ) mergedParent.getChildren().iterator().next();
+ assertEquals( "unexpected child version increment", child.getVersion(), mergedChild.getVersion() );
+
+ ///////////////////////////////////////////////////////////////////////
+ // as a control measure, now update the node once it is loaded and
+ // make sure we get an update as a result...
+ s = openSession();
+ s.beginTransaction();
+ persistentParent = ( VersionedEntity ) s.get( VersionedEntity.class, parent.getId() );
+ persistentParent.setName( "new name" );
+ persistentParent.getChildren().add( new VersionedEntity( "child2", "new child" ) );
+ persistentParent = ( VersionedEntity ) s.merge( persistentParent );
+ s.getTransaction().commit();
+ s.close();
+ assertUpdateCount( 1 );
+ assertInsertCount( 1 );
+ ///////////////////////////////////////////////////////////////////////
+
+// cleanup();
+ }
+
+
public void testPersistThenMergeInSameTxnWithVersion() {
Session s = openSession();
Transaction tx = s.beginTransaction();

Index: src/org/hibernate/type/CollectionType.java
===================================================================
— src/org/hibernate/type/CollectionType.java (revision 14669)
+++ src/org/hibernate/type/CollectionType.java (working copy)
@@ -521,7 +521,18 @@
//get the elements back into the target
//TODO: this is a little inefficient, don't need to do a whole
// deep replaceElements() call

  • replaceElements( result, target, owner, copyCache, session );
    + if ( target instanceof PersistentCollection ) {
    + if ( ! ( ( PersistentCollection ) target ).isDirty() ) {
    + replaceElements( result, target, owner, copyCache, session );
    + ( ( PersistentCollection ) target ).clearDirty();
    + }
    + else {
    + replaceElements( result, target, owner, copyCache, session );
    + }
    + }
    + else {
    + replaceElements( result, target, owner, copyCache, session );
    + }
    result = target;
    }

Environment

3.2.4.sp1, Oracle 10g

Activity

Show:
Ben Groeneveld
June 14, 2008, 12:12 AM

We believe to have this same issue and worked on this with Robb G. of JBoss who communicated this allegedly to Peter M as JBoss support issue 157937 "database version number does not match passivated version number" - no resolution as 12/4/2007.

Steve Ebersole
July 1, 2008, 11:55 PM

applied something similar... thanks for tracking it down

Chris Bredesen
July 9, 2008, 4:44 PM

Also fixing this in EAP releases:
http://jira.jboss.org/jira/browse/JBPAPP-969

Chris Bredesen
July 9, 2008, 7:22 PM

Assignee

Steve Ebersole

Reporter

RayC

Fix versions

Labels

None

backPortable

None

Suitable for new contributors

None

Requires Release Note

None

Pull Request

None

backportDecision

None

Components

Affects versions

Priority

Major
Configure