[hibernate-commits] Hibernate SVN: r10741 - in branches/Branch_3_2/Hibernate3: src/org/hibernate/type test/org/hibernate/test/ops

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Mon Nov 6 17:14:34 EST 2006


Author: steve.ebersole at jboss.com
Date: 2006-11-06 17:14:32 -0500 (Mon, 06 Nov 2006)
New Revision: 10741

Modified:
   branches/Branch_3_2/Hibernate3/src/org/hibernate/type/CollectionType.java
   branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/MergeTest.java
   branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/OptLockEntity.hbm.xml
   branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/VersionedEntity.java
Log:
HHH-1401 : merge operation casuing unecessary updates

Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/type/CollectionType.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/type/CollectionType.java	2006-11-06 22:13:39 UTC (rev 10740)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/type/CollectionType.java	2006-11-06 22:14:32 UTC (rev 10741)
@@ -397,15 +397,25 @@
 	}
 
 	/**
-	 * Replace the elements of a collection with the elements of another collection
+	 * Replace the elements of a collection with the elements of another collection.
+	 *
+	 * @param original The 'source' of the replacement elements (where we copy from)
+	 * @param target The target of the replacement elements (where we copy to)
+	 * @param owner The owner of the collection being merged
+	 * @param copyCache The map of elements already replaced.
+	 * @param session The session from which the merge event originated.
+	 * @return The merged collection.
 	 */
-	public Object replaceElements(Object original, Object target, Object owner, Map copyCache,
-			SessionImplementor session) throws HibernateException {
-
+	public Object replaceElements(
+			Object original,
+			Object target,
+			Object owner,
+			Map copyCache,
+			SessionImplementor session) {
 		// TODO: does not work for EntityMode.DOM4J yet!
-
-		java.util.Collection result = (java.util.Collection) target;
-
+		java.util.Collection result = ( java.util.Collection ) target;
+		final boolean isPC = ( result instanceof PersistentCollection );
+		final boolean wasOriginalDirty = ( original instanceof PersistentCollection && ( ( PersistentCollection ) original ).isDirty() );
 		result.clear();
 
 		// copy elements into newly empty target collection
@@ -414,9 +424,12 @@
 		while ( iter.hasNext() ) {
 			result.add( elemType.replace( iter.next(), null, session, owner, copyCache ) );
 		}
-		
+
+		if ( result instanceof PersistentCollection && !wasOriginalDirty ) {
+			( ( PersistentCollection ) result ).clearDirty();
+		}
+
 		return result;
-
 	}
 
 	/**
@@ -439,24 +452,25 @@
 	 *
 	 * @param anticipatedSize The anticipated size of the instaniated collection
 	 * after we are done populating it.
+	 * @return A newly instantiated collection to be wrapped.
 	 */
 	public abstract Object instantiate(int anticipatedSize);
 
+	/**
+	 * {@inheritDoc}
+	 */
 	public Object replace(
 			final Object original, 
 			final Object target,
 			final SessionImplementor session, 
 			final Object owner, 
-			final Map copyCache)
-	throws HibernateException {
-
+			final Map copyCache) throws HibernateException {
 		if ( original == null ) {
 			return null;
 		}
 		if ( !Hibernate.isInitialized( original ) ) {
 			return target;
 		}
-		//if ( original == target ) return target; // can't do this, since need to merge element references
 
 		// for a null target, or a target which is the same as the original, we
 		// need to put the merged elements in a new collection
@@ -475,7 +489,6 @@
 		}
 				
 		return result;
-
 	}
 
 	/**

Modified: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/MergeTest.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/MergeTest.java	2006-11-06 22:13:39 UTC (rev 10740)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/MergeTest.java	2006-11-06 22:14:32 UTC (rev 10741)
@@ -2,6 +2,7 @@
 package org.hibernate.test.ops;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 
 import junit.framework.Test;
 import junit.framework.TestSuite;
@@ -24,6 +25,167 @@
 		super(str);
 	}
 
+    public void testNoExtraUpdatesOnMerge() throws Exception {
+		Session s = openSession();
+        s.beginTransaction();
+		Node node = new Node( "test" );
+		s.persist( node );
+		s.getTransaction().commit();
+		s.close();
+
+		clearCounts();
+
+		// node 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();
+		node = ( Node ) s.merge( node );
+		s.getTransaction().commit();
+		s.close();
+
+		assertUpdateCount( 0 );
+		assertInsertCount( 0 );
+
+		///////////////////////////////////////////////////////////////////////
+		// as a control measure, now update the node while it is detached and
+		// make sure we get an update as a result...
+		node.setDescription( "new description" );
+		s = openSession();
+		s.beginTransaction();
+		node = ( Node ) s.merge( node );
+		s.getTransaction().commit();
+		s.close();
+		assertUpdateCount( 1 );
+		assertInsertCount( 0 );
+		///////////////////////////////////////////////////////////////////////
+
+		cleanup();
+    }
+
+	public void testNoExtraUpdatesOnMergeWithCollection() throws Exception {
+		Session s = openSession();
+        s.beginTransaction();
+		Node parent = new Node( "parent" );
+		Node child = new Node( "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();
+		parent = ( Node ) s.merge( parent );
+		s.getTransaction().commit();
+		s.close();
+
+		assertUpdateCount( 0 );
+		assertInsertCount( 0 );
+
+		///////////////////////////////////////////////////////////////////////
+		// as a control measure, now update the node while it is detached and
+		// make sure we get an update as a result...
+		( ( Node ) parent.getChildren().iterator().next() ).setDescription( "child's new description" );
+		parent.getChildren().add( new Node( "second child" ) );
+		s = openSession();
+		s.beginTransaction();
+		parent = ( Node ) s.merge( parent );
+		s.getTransaction().commit();
+		s.close();
+		assertUpdateCount( 1 );
+		assertInsertCount( 1 );
+		///////////////////////////////////////////////////////////////////////
+
+		cleanup();
+	}
+
+    public void testNoExtraUpdatesOnMergeVersioned() throws Exception {
+		Session s = openSession();
+        s.beginTransaction();
+		VersionedEntity entity = new VersionedEntity( "entity", "entity" );
+		s.persist( entity );
+		s.getTransaction().commit();
+		s.close();
+
+		clearCounts();
+
+		// entity 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();
+		VersionedEntity mergedEntity = ( VersionedEntity ) s.merge( entity );
+		s.getTransaction().commit();
+		s.close();
+
+		assertUpdateCount( 0 );
+		assertInsertCount( 0 );
+        assertEquals( "unexpected version increment", entity.getVersion(), mergedEntity.getVersion() );
+
+
+		///////////////////////////////////////////////////////////////////////
+		// as a control measure, now update the node while it is detached and
+		// make sure we get an update as a result...
+		entity.setName( "new name" );
+		s = openSession();
+		s.beginTransaction();
+		entity = ( VersionedEntity ) s.merge( entity );
+		s.getTransaction().commit();
+		s.close();
+		assertUpdateCount( 1 );
+		assertInsertCount( 0 );
+		///////////////////////////////////////////////////////////////////////
+
+		cleanup();
+    }
+
+    public void testNoExtraUpdatesOnMergeVersionedWithCollection() 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();
+		VersionedEntity mergedParent = ( VersionedEntity ) s.merge( parent );
+		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 while it is detached and
+		// make sure we get an update as a result...
+		mergedParent.setName( "new name" );
+		mergedParent.getChildren().add( new VersionedEntity( "child2", "new child" ) );
+		s = openSession();
+		s.beginTransaction();
+		parent = ( VersionedEntity ) s.merge( mergedParent );
+		s.getTransaction().commit();
+		s.close();
+		assertUpdateCount( 1 );
+		assertInsertCount( 1 );
+		///////////////////////////////////////////////////////////////////////
+
+		cleanup();
+    }
+
 	public void testPersistThenMergeInSameTxnWithVersion() {
 		Session s = openSession();
 		Transaction tx = s.beginTransaction();
@@ -43,11 +205,7 @@
 		tx.commit();
 		s.close();
 
-		s = openSession();
-		tx = s.beginTransaction();
-		s.delete( entity );
-		tx.commit();
-		s.close();
+		cleanup();
 	}
 
 	public void testPersistThenMergeInSameTxnWithTimestamp() {
@@ -69,11 +227,7 @@
 		tx.commit();
 		s.close();
 
-		s = openSession();
-		tx = s.beginTransaction();
-		s.delete( entity );
-		tx.commit();
-		s.close();
+		cleanup();
 	}
 	
 	public void testMergeDeepTree() {
@@ -123,7 +277,7 @@
 		assertInsertCount(2);
 		assertUpdateCount(0);
 		clearCounts();
-		
+
 		s = openSession();
 		tx = s.beginTransaction();
 		s.delete(grandchild);
@@ -134,7 +288,7 @@
 		s.delete(root);
 		tx.commit();
 		s.close();
-	
+
 	}
 	
 	public void testMergeDeepTreeWithGeneratedId() {
@@ -188,7 +342,7 @@
 		assertInsertCount(2);
 		assertUpdateCount(0);
 		clearCounts();
-		
+
 		s = openSession();
 		tx = s.beginTransaction();
 		s.createQuery("delete from NumberedNode where name like 'grand%'").executeUpdate();
@@ -196,7 +350,7 @@
 		s.createQuery("delete from NumberedNode").executeUpdate();
 		tx.commit();
 		s.close();
-	
+
 	}
 	
 	public void testMergeTree() {
@@ -230,14 +384,8 @@
 		
 		assertInsertCount(1);
 		assertUpdateCount(2);
-		
-		s = openSession();
-		tx = s.beginTransaction();
-		s.createQuery("delete from Node where parent is not null").executeUpdate();
-		s.createQuery("delete from Node").executeUpdate();
-		tx.commit();
-		s.close();
-		
+
+		cleanup();
 	}
 		
 	public void testMergeTreeWithGeneratedId() {
@@ -271,14 +419,8 @@
 		
 		assertInsertCount(1);
 		assertUpdateCount(2);
-		
-		s = openSession();
-		tx = s.beginTransaction();
-		s.createQuery("delete from NumberedNode where parent is not null").executeUpdate();
-		s.createQuery("delete from NumberedNode").executeUpdate();
-		tx.commit();
-		s.close();
 
+		cleanup();
 	}
 		
 	public void testMergeManaged() {
@@ -317,11 +459,8 @@
 				.uniqueResult(), 
 			new Integer(2) 
 		);
-		s.delete(root);
-		s.delete(mergedChild);
-		tx.commit();
-		s.close();
-		
+
+		cleanup();
 	}
 	
 	public void testRecursiveMergeTransient() {
@@ -339,7 +478,9 @@
 		s.clear();
 		s.merge( jboss.getEmployees().iterator().next() );
 		tx.commit();
-		s.close();		
+		s.close();
+
+		cleanup();
 	}
 
 	public void testDeleteAndMerge() throws Exception {
@@ -361,20 +502,22 @@
 		s.merge( jboss );
 		s.getTransaction().commit();
 		s.close();
+
+		cleanup();
 	}
 	
 	private void clearCounts() {
 		getSessions().getStatistics().clear();
 	}
 	
-	private void assertInsertCount(int count) {
+	private void assertInsertCount(int expected) {
 		int inserts = (int) getSessions().getStatistics().getEntityInsertCount();
-		assertEquals(count, inserts);
+		assertEquals( "unexpected insert count", expected, inserts );
 	}
 		
-	private void assertUpdateCount(int count) {
+	private void assertUpdateCount(int expected) {
 		int updates = (int) getSessions().getStatistics().getEntityUpdateCount();
-		assertEquals(count, updates);
+		assertEquals( "unexpected update counts", expected, updates );
 	}
 		
 	protected void configure(Configuration cfg) {
@@ -390,5 +533,25 @@
 		return new TestSuite(MergeTest.class);
 	}
 
+	private void cleanup() {
+		Session s = openSession();
+		s.beginTransaction();
+		s.createQuery("delete from NumberedNode where parent is not null").executeUpdate();
+		s.createQuery("delete from NumberedNode").executeUpdate();
+		s.createQuery("delete from Node where parent is not null").executeUpdate();
+		s.createQuery("delete from Node").executeUpdate();
+		s.createQuery("delete from VersionedEntity where parent is not null").executeUpdate();
+		s.createQuery("delete from VersionedEntity").executeUpdate();
+		s.createQuery("delete from TimestampedEntity").executeUpdate();
+
+		Iterator itr = s.createQuery( "from Employer" ).list().iterator();
+		while ( itr.hasNext() ) {
+			final Employer employer = ( Employer ) itr.next();
+			s.delete( employer );
+		}
+
+		s.getTransaction().commit();
+		s.close();
+	}
 }
 

Modified: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/OptLockEntity.hbm.xml
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/OptLockEntity.hbm.xml	2006-11-06 22:13:39 UTC (rev 10740)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/OptLockEntity.hbm.xml	2006-11-06 22:14:32 UTC (rev 10741)
@@ -15,6 +15,13 @@
 		</id>
         <version name="version" column="VERS" type="long" />
         <property name="name" column="NAME" type="string" />
+        <many-to-one name="parent" class="VersionedEntity"/>
+        <set name="children"
+			inverse="true"
+			cascade="persist,merge,save-update,evict">
+			<key column="parent"/>
+			<one-to-many class="VersionedEntity"/>
+		</set>
     </class>
 
 	<class name="TimestampedEntity" table="T_ENTITY">

Modified: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/VersionedEntity.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/VersionedEntity.java	2006-11-06 22:13:39 UTC (rev 10740)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/VersionedEntity.java	2006-11-06 22:14:32 UTC (rev 10741)
@@ -1,7 +1,10 @@
 package org.hibernate.test.ops;
 
+import java.util.Set;
+import java.util.HashSet;
+
 /**
- * todo: describe VersionedEntity
+ * VersionedEntity
  *
  * @author Steve Ebersole
  */
@@ -10,6 +13,9 @@
 	private String name;
 	private long version;
 
+	private VersionedEntity parent;
+	private Set children = new HashSet();
+
 	public VersionedEntity() {
 	}
 
@@ -41,4 +47,20 @@
 	public void setVersion(long version) {
 		this.version = version;
 	}
+
+	public VersionedEntity getParent() {
+		return parent;
+	}
+
+	public void setParent(VersionedEntity parent) {
+		this.parent = parent;
+	}
+
+	public Set getChildren() {
+		return children;
+	}
+
+	public void setChildren(Set children) {
+		this.children = children;
+	}
 }




More information about the hibernate-commits mailing list