diff --git a/core/src/main/java/org/hibernate/event/def/DefaultMergeEventListener.java b/core/src/main/java/org/hibernate/event/def/DefaultMergeEventListener.java index 73ecfa2445..4392437959 100755 --- a/core/src/main/java/org/hibernate/event/def/DefaultMergeEventListener.java +++ b/core/src/main/java/org/hibernate/event/def/DefaultMergeEventListener.java @@ -93,8 +93,8 @@ public class DefaultMergeEventListener extends AbstractSaveEventListener // TODO: cache the entity name somewhere so that it is available to this exception // entity name will not be available for non-POJO entities } - if ( entry.getStatus() != Status.MANAGED ) { - throw new AssertionFailure( "Merged entity does not have status set to MANAGED; "+entry+" status="+entry.getStatus() ); + if ( entry.getStatus() != Status.MANAGED && entry.getStatus() != Status.READ_ONLY ) { + throw new AssertionFailure( "Merged entity does not have status set to MANAGED or READ_ONLY; "+entry+" status="+entry.getStatus() ); } } } diff --git a/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java b/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java index 30d115864a..744019508c 100755 --- a/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java +++ b/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java @@ -1,8 +1,35 @@ //$Id: ImmutableTest.java 10977 2006-12-12 23:28:04Z steve.ebersole@jboss.com $ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Middleware LLC. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + * + */ package org.hibernate.test.immutable; +import java.util.Iterator; + import junit.framework.Test; +import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.criterion.Projections; @@ -53,8 +80,11 @@ public class ImmutableTest extends FunctionalTestCase { c = (Contract) s.createCriteria(Contract.class).uniqueResult(); assertEquals( c.getCustomerName(), "gavin" ); assertEquals( c.getVariations().size(), 2 ); - cv1 = (ContractVariation) c.getVariations().iterator().next(); + Iterator it = c.getVariations().iterator(); + cv1 = (ContractVariation) it.next(); assertEquals( cv1.getText(), "expensive" ); + cv2 = (ContractVariation) it.next(); + assertEquals( cv2.getText(), "more expensive" ); s.delete(c); assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) ); assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) ); @@ -62,5 +92,239 @@ public class ImmutableTest extends FunctionalTestCase { s.close(); } + public void testImmutableParentEntityWithUpdate() { + Contract c = new Contract("gavin", "phone"); + ContractVariation cv1 = new ContractVariation(1, c); + cv1.setText("expensive"); + ContractVariation cv2 = new ContractVariation(2, c); + cv2.setText("more expensive"); + Session s = openSession(); + Transaction t = s.beginTransaction(); + s.persist(c); + t.commit(); + s.close(); + + s = openSession(); + t = s.beginTransaction(); + c.setCustomerName("foo bar"); + s.update( c ); + t.commit(); + s.close(); + + s = openSession(); + t = s.beginTransaction(); + c = (Contract) s.createCriteria(Contract.class).uniqueResult(); + assertEquals( c.getCustomerName(), "gavin" ); + assertEquals( c.getVariations().size(), 2 ); + Iterator it = c.getVariations().iterator(); + cv1 = (ContractVariation) it.next(); + assertEquals( cv1.getText(), "expensive" ); + cv2 = (ContractVariation) it.next(); + assertEquals( cv2.getText(), "more expensive" ); + s.delete(c); + assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) ); + assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) ); + t.commit(); + s.close(); + } + + public void testImmutableChildEntityWithUpdate() { + Contract c = new Contract("gavin", "phone"); + ContractVariation cv1 = new ContractVariation(1, c); + cv1.setText("expensive"); + ContractVariation cv2 = new ContractVariation(2, c); + cv2.setText("more expensive"); + Session s = openSession(); + Transaction t = s.beginTransaction(); + s.persist(c); + t.commit(); + s.close(); + + s = openSession(); + t = s.beginTransaction(); + cv1 = (ContractVariation) c.getVariations().iterator().next(); + cv1.setText("blah blah"); + s.update( c ); + t.commit(); + s.close(); + + s = openSession(); + t = s.beginTransaction(); + c = (Contract) s.createCriteria(Contract.class).uniqueResult(); + assertEquals( c.getCustomerName(), "gavin" ); + assertEquals( c.getVariations().size(), 2 ); + Iterator it = c.getVariations().iterator(); + cv1 = (ContractVariation) it.next(); + assertEquals( cv1.getText(), "expensive" ); + cv2 = (ContractVariation) it.next(); + assertEquals( cv2.getText(), "more expensive" ); + s.delete(c); + assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) ); + assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) ); + t.commit(); + s.close(); + } + + public void testImmutableCollectionWithUpdate() { + Contract c = new Contract("gavin", "phone"); + ContractVariation cv1 = new ContractVariation(1, c); + cv1.setText("expensive"); + ContractVariation cv2 = new ContractVariation(2, c); + cv2.setText("more expensive"); + Session s = openSession(); + Transaction t = s.beginTransaction(); + s.persist(c); + t.commit(); + s.close(); + + s = openSession(); + t = s.beginTransaction(); + c.getVariations().add( new ContractVariation(3, c) ); + try { + s.update( c ); + fail( "should have failed because reassociated object has a dirty collection"); + } + catch ( HibernateException ex ) { + // expected + } + finally { + t.rollback(); + s.close(); + } + + s = openSession(); + t = s.beginTransaction(); + c = (Contract) s.createCriteria(Contract.class).uniqueResult(); + assertEquals( c.getCustomerName(), "gavin" ); + assertEquals( c.getVariations().size(), 2 ); + Iterator it = c.getVariations().iterator(); + cv1 = (ContractVariation) it.next(); + assertEquals( cv1.getText(), "expensive" ); + cv2 = (ContractVariation) it.next(); + assertEquals( cv2.getText(), "more expensive" ); + s.delete(c); + assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) ); + assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) ); + t.commit(); + s.close(); + } + + public void testImmutableParentEntityWithMerge() { + Contract c = new Contract("gavin", "phone"); + ContractVariation cv1 = new ContractVariation(1, c); + cv1.setText("expensive"); + ContractVariation cv2 = new ContractVariation(2, c); + cv2.setText("more expensive"); + Session s = openSession(); + Transaction t = s.beginTransaction(); + s.persist(c); + t.commit(); + s.close(); + + s = openSession(); + t = s.beginTransaction(); + c.setCustomerName("foo bar"); + s.merge( c ); + t.commit(); + s.close(); + + s = openSession(); + t = s.beginTransaction(); + c = (Contract) s.createCriteria(Contract.class).uniqueResult(); + assertEquals( c.getCustomerName(), "gavin" ); + assertEquals( c.getVariations().size(), 2 ); + Iterator it = c.getVariations().iterator(); + cv1 = (ContractVariation) it.next(); + assertEquals( cv1.getText(), "expensive" ); + cv2 = (ContractVariation) it.next(); + assertEquals( cv2.getText(), "more expensive" ); + s.delete(c); + assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) ); + assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) ); + t.commit(); + s.close(); + } + + public void testImmutableChildEntityWithMerge() { + Contract c = new Contract("gavin", "phone"); + ContractVariation cv1 = new ContractVariation(1, c); + cv1.setText("expensive"); + ContractVariation cv2 = new ContractVariation(2, c); + cv2.setText("more expensive"); + Session s = openSession(); + Transaction t = s.beginTransaction(); + s.persist(c); + t.commit(); + s.close(); + + s = openSession(); + t = s.beginTransaction(); + cv1 = (ContractVariation) c.getVariations().iterator().next(); + cv1.setText("blah blah"); + s.merge( c ); + t.commit(); + s.close(); + + s = openSession(); + t = s.beginTransaction(); + c = (Contract) s.createCriteria(Contract.class).uniqueResult(); + assertEquals( c.getCustomerName(), "gavin" ); + assertEquals( c.getVariations().size(), 2 ); + Iterator it = c.getVariations().iterator(); + cv1 = (ContractVariation) it.next(); + assertEquals( cv1.getText(), "expensive" ); + cv2 = (ContractVariation) it.next(); + assertEquals( cv2.getText(), "more expensive" ); + s.delete(c); + assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) ); + assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) ); + t.commit(); + s.close(); + } + + public void testImmutableCollectionWithMerge() { + Contract c = new Contract("gavin", "phone"); + ContractVariation cv1 = new ContractVariation(1, c); + cv1.setText("expensive"); + ContractVariation cv2 = new ContractVariation(2, c); + cv2.setText("more expensive"); + Session s = openSession(); + Transaction t = s.beginTransaction(); + s.persist(c); + t.commit(); + s.close(); + + s = openSession(); + t = s.beginTransaction(); + c.getVariations().add( new ContractVariation(3, c) ); + s.merge( c ); + try { + t.commit(); + fail( "should have failed because an immutable collection was changed"); + } + catch ( HibernateException ex ) { + // expected + t.rollback(); + } + finally { + s.close(); + } + + s = openSession(); + t = s.beginTransaction(); + c = (Contract) s.createCriteria(Contract.class).uniqueResult(); + assertEquals( c.getCustomerName(), "gavin" ); + assertEquals( c.getVariations().size(), 2 ); + Iterator it = c.getVariations().iterator(); + cv1 = (ContractVariation) it.next(); + assertEquals( cv1.getText(), "expensive" ); + cv2 = (ContractVariation) it.next(); + assertEquals( cv2.getText(), "more expensive" ); + s.delete(c); + assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) ); + assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Integer(0) ); + t.commit(); + s.close(); + } } diff --git a/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java b/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java index cb3ca4d640..b708ff2bf1 100755 --- a/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java +++ b/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java @@ -1,4 +1,28 @@ //$Id: ReadOnlyTest.java 10977 2006-12-12 23:28:04Z steve.ebersole@jboss.com $ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Middleware LLC. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + * + */ package org.hibernate.test.readonly; import java.math.BigDecimal; @@ -148,5 +172,39 @@ public class ReadOnlyTest extends FunctionalTestCase { s.getTransaction().commit(); s.close(); } + + public void testMergeWithReadOnlyEntity() { + + Session s = openSession(); + s.setCacheMode(CacheMode.IGNORE); + Transaction t = s.beginTransaction(); + DataPoint dp = new DataPoint(); + dp.setX( new BigDecimal(0.1d).setScale(19, BigDecimal.ROUND_DOWN) ); + dp.setY( new BigDecimal( Math.cos( dp.getX().doubleValue() ) ).setScale(19, BigDecimal.ROUND_DOWN) ); + s.save(dp); + t.commit(); + s.close(); + + dp.setDescription( "description" ); + + s = openSession(); + s.setCacheMode(CacheMode.IGNORE); + t = s.beginTransaction(); + DataPoint dpManaged = ( DataPoint ) s.get( DataPoint.class, new Long( dp.getId() ) ); + s.setReadOnly( dpManaged, true ); + DataPoint dpMerged = ( DataPoint ) s.merge( dp ); + assertSame( dpManaged, dpMerged ); + t.commit(); + s.close(); + + s = openSession(); + t = s.beginTransaction(); + dpManaged = ( DataPoint ) s.get( DataPoint.class, new Long( dp.getId() ) ); + assertNull( dpManaged.getDescription() ); + s.delete( dpManaged ); + t.commit(); + s.close(); + + } } diff --git a/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java b/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java new file mode 100644 index 0000000000..9e862ec92c --- /dev/null +++ b/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java @@ -0,0 +1,481 @@ +//$Id: $ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Middleware LLC. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + * + */ +package org.hibernate.test.readonly; + +import junit.framework.Test; + +import org.hibernate.Session; +import org.hibernate.junit.functional.FunctionalTestCase; +import org.hibernate.junit.functional.FunctionalTestClassTestSuite; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; + +/** + * @author Gail Badner + */ +public class ReadOnlyVersionedNodesTest extends FunctionalTestCase { + + public ReadOnlyVersionedNodesTest(String str) { + super( str ); + } + + public String[] getMappings() { + return new String[] { "readonly/VersionedNode.hbm.xml" }; + } + + public String getCacheConcurrencyStrategy() { + return null; + } + + public void configure(Configuration cfg) { + cfg.setProperty( Environment.GENERATE_STATISTICS, "true"); + cfg.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" ); + } + + public static Test suite() { + return new FunctionalTestClassTestSuite( ReadOnlyVersionedNodesTest.class ); + } + + public void testSetReadOnlyTrueAndFalse() throws Exception { + Session s = openSession(); + s.beginTransaction(); + VersionedNode node = new VersionedNode( "node", "node" ); + s.persist( node ); + s.getTransaction().commit(); + s.close(); + + clearCounts(); + + s = openSession(); + + s.beginTransaction(); + node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() ); + s.setReadOnly( node, true ); + node.setName( "node-name" ); + s.getTransaction().commit(); + + assertUpdateCount( 0 ); + assertInsertCount( 0 ); + + // the changed name is still in node + assertEquals( "node-name", node.getName() ); + + s.beginTransaction(); + node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() ); + // the changed name is still in the session + assertEquals( "node-name", node.getName() ); + s.refresh( node ); + // after refresh, the name reverts to the original value + assertEquals( "node", node.getName() ); + node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() ); + assertEquals( "node", node.getName() ); + s.getTransaction().commit(); + + s.close(); + + assertUpdateCount( 0 ); + assertInsertCount( 0 ); + + s = openSession(); + s.beginTransaction(); + node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() ); + assertEquals( "node", node.getName() ); + s.setReadOnly( node, true ); + node.setName( "diff-node-name" ); + s.flush(); + assertEquals( "diff-node-name", node.getName() ); + s.refresh( node ); + assertEquals( "node", node.getName() ); + s.setReadOnly( node, false ); + node.setName( "diff-node-name" ); + s.getTransaction().commit(); + s.close(); + + assertUpdateCount( 1 ); + assertInsertCount( 0 ); + + s = openSession(); + s.beginTransaction(); + node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() ); + assertEquals( "diff-node-name", node.getName() ); + assertEquals( 1, node.getVersion() ); + s.delete( node ); + s.getTransaction().commit(); + s.close(); + } + + public void testAddNewChildToReadOnlyParent() throws Exception { + Session s = openSession(); + s.beginTransaction(); + VersionedNode parent = new VersionedNode( "parent", "parent" ); + s.persist( parent ); + s.getTransaction().commit(); + s.close(); + + clearCounts(); + + s = openSession(); + s.beginTransaction(); + VersionedNode parentManaged = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() ); + s.setReadOnly( parentManaged, true ); + parentManaged.setName( "new parent name" ); + VersionedNode child = new VersionedNode( "child", "child"); + parentManaged.addChild( child ); + s.getTransaction().commit(); + s.close(); + + assertUpdateCount( 0 ); + assertInsertCount( 0 ); + + s = openSession(); + s.beginTransaction(); + parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() ); + assertEquals( "parent", parent.getName() ); + assertEquals( 0, parent.getChildren().size() ); + assertEquals( 0, parent.getVersion() ); + child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() ); + assertNull( child ); + s.delete( parent ); + s.getTransaction().commit(); + s.close(); + } + + public void testUpdateParentWithNewChildCommitWithReadOnlyParent() throws Exception { + Session s = openSession(); + s.beginTransaction(); + VersionedNode parent = new VersionedNode( "parent", "parent" ); + s.persist( parent ); + s.getTransaction().commit(); + s.close(); + + clearCounts(); + + parent.setName( "new parent name" ); + VersionedNode child = new VersionedNode( "child", "child"); + parent.addChild( child ); + + s = openSession(); + s.beginTransaction(); + s.update( parent ); + s.setReadOnly( parent, true ); + s.getTransaction().commit(); + s.close(); + + assertUpdateCount( 0 ); + assertInsertCount( 1 ); + + s = openSession(); + s.beginTransaction(); + parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() ); + child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() ); + assertEquals( parent.getName(), "parent" ); + assertEquals( 1, parent.getChildren().size() ); + assertEquals( 0, parent.getVersion() ); + assertSame( parent, child.getParent() ); + assertSame( child, parent.getChildren().iterator().next() ); + assertEquals( 0, child.getVersion() ); + s.delete( parent ); + s.delete( child ); + s.getTransaction().commit(); + s.close(); + } + + public void testMergeDetachedParentWithNewChildCommitWithReadOnlyParent() throws Exception { + Session s = openSession(); + s.beginTransaction(); + VersionedNode parent = new VersionedNode( "parent", "parent" ); + s.persist( parent ); + s.getTransaction().commit(); + s.close(); + + clearCounts(); + + parent.setName( "new parent name" ); + VersionedNode child = new VersionedNode( "child", "child"); + parent.addChild( child ); + + s = openSession(); + s.beginTransaction(); + parent = ( VersionedNode ) s.merge( parent ); + s.setReadOnly( parent, true ); + s.getTransaction().commit(); + s.close(); + + assertUpdateCount( 0 ); + assertInsertCount( 1 ); + + s = openSession(); + s.beginTransaction(); + parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() ); + child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() ); + assertEquals( parent.getName(), "parent" ); + assertEquals( 1, parent.getChildren().size() ); + assertEquals( 0, parent.getVersion() ); + assertSame( parent, child.getParent() ); + assertSame( child, parent.getChildren().iterator().next() ); + assertEquals( 0, child.getVersion() ); + s.delete( parent ); + s.delete( child ); + s.getTransaction().commit(); + s.close(); + } + + public void testGetParentMakeReadOnlyThenMergeDetachedParentWithNewChildC() throws Exception { + Session s = openSession(); + s.beginTransaction(); + VersionedNode parent = new VersionedNode( "parent", "parent" ); + s.persist( parent ); + s.getTransaction().commit(); + s.close(); + + clearCounts(); + + parent.setName( "new parent name" ); + VersionedNode child = new VersionedNode( "child", "child"); + parent.addChild( child ); + + s = openSession(); + s.beginTransaction(); + VersionedNode parentManaged = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() ); + s.setReadOnly( parentManaged, true ); + VersionedNode parentMerged = ( VersionedNode ) s.merge( parent ); + assertSame( parentManaged, parentMerged ); + s.getTransaction().commit(); + s.close(); + + assertUpdateCount( 0 ); + assertInsertCount( 1 ); + + s = openSession(); + s.beginTransaction(); + parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() ); + child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() ); + assertEquals( parent.getName(), "parent" ); + assertEquals( 1, parent.getChildren().size() ); + assertEquals( 0, parent.getVersion() ); + assertSame( parent, child.getParent() ); + assertSame( child, parent.getChildren().iterator().next() ); + assertEquals( 0, child.getVersion() ); + s.delete( parent ); + s.delete( child ); + s.getTransaction().commit(); + s.close(); + } + + + public void testAddNewParentToReadOnlyChild() throws Exception { + Session s = openSession(); + s.beginTransaction(); + VersionedNode child = new VersionedNode( "child", "child" ); + s.persist( child ); + s.getTransaction().commit(); + s.close(); + + clearCounts(); + + s = openSession(); + s.beginTransaction(); + VersionedNode childManaged = ( VersionedNode ) s.get( VersionedNode.class, child.getId() ); + s.setReadOnly( childManaged, true ); + childManaged.setName( "new child name" ); + VersionedNode parent = new VersionedNode( "parent", "parent"); + parent.addChild( childManaged ); + s.getTransaction().commit(); + s.close(); + + assertUpdateCount( 0 ); + assertInsertCount( 0 ); + + s = openSession(); + s.beginTransaction(); + child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() ); + assertEquals( "child", child.getName() ); + assertNull( child.getParent() ); + assertEquals( 0, child.getVersion() ); + parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() ); + assertNull( parent ); + s.delete( child ); + s.getTransaction().commit(); + s.close(); + } + + public void testUpdateChildWithNewParentCommitWithReadOnlyChild() throws Exception { + Session s = openSession(); + s.beginTransaction(); + VersionedNode child = new VersionedNode( "child", "child" ); + s.persist( child ); + s.getTransaction().commit(); + s.close(); + + clearCounts(); + + child.setName( "new child name" ); + VersionedNode parent = new VersionedNode( "parent", "parent"); + parent.addChild( child ); + + s = openSession(); + s.beginTransaction(); + s.update( child ); + s.setReadOnly( child, true ); + s.getTransaction().commit(); + s.close(); + + assertUpdateCount( 0 ); + assertInsertCount( 1 ); + + s = openSession(); + s.beginTransaction(); + parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() ); + child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() ); + assertEquals( child.getName(), "child" ); + assertNull( child.getParent() ); + assertEquals( 0, child.getVersion() ); + assertNotNull( parent ); + assertEquals( 0, parent.getChildren().size() ); + assertEquals( 0, parent.getVersion() ); + s.delete( parent ); + s.delete( child ); + s.getTransaction().commit(); + s.close(); + } + + public void testMergeDetachedChildWithNewParentCommitWithReadOnlyChild() throws Exception { + Session s = openSession(); + s.beginTransaction(); + VersionedNode child = new VersionedNode( "child", "child" ); + s.persist( child ); + s.getTransaction().commit(); + s.close(); + + clearCounts(); + + child.setName( "new child name" ); + VersionedNode parent = new VersionedNode( "parent", "parent"); + parent.addChild( child ); + + s = openSession(); + s.beginTransaction(); + child = ( VersionedNode ) s.merge( child ); + s.setReadOnly( child, true ); + s.getTransaction().commit(); + s.close(); + + assertUpdateCount( 1 ); + assertInsertCount( 1 ); + + s = openSession(); + s.beginTransaction(); + parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() ); + child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() ); + assertEquals( child.getName(), "child" ); + assertNull( child.getParent() ); + assertEquals( 0, child.getVersion() ); + assertNotNull( parent ); + assertEquals( 0, parent.getChildren().size() ); + assertEquals( 1, parent.getVersion() ); // hmmm, why is was version updated? + s.delete( parent ); + s.delete( child ); + s.getTransaction().commit(); + s.close(); + } + + public void testGetChildMakeReadOnlyThenMergeDetachedChildWithNewParent() throws Exception { + Session s = openSession(); + s.beginTransaction(); + VersionedNode child = new VersionedNode( "child", "child" ); + s.persist( child ); + s.getTransaction().commit(); + s.close(); + + clearCounts(); + + child.setName( "new child name" ); + VersionedNode parent = new VersionedNode( "parent", "parent"); + parent.addChild( child ); + + s = openSession(); + s.beginTransaction(); + VersionedNode childManaged = ( VersionedNode ) s.get( VersionedNode.class, child.getId() ); + s.setReadOnly( childManaged, true ); + VersionedNode childMerged = ( VersionedNode ) s.merge( child ); + assertSame( childManaged, childMerged ); + s.getTransaction().commit(); + s.close(); + + assertUpdateCount( 1 ); + assertInsertCount( 1 ); + + s = openSession(); + s.beginTransaction(); + parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() ); + child = ( VersionedNode ) s.get( VersionedNode.class, child.getId() ); + assertEquals( child.getName(), "child" ); + assertNull( child.getParent() ); + assertEquals( 0, child.getVersion() ); + assertNotNull( parent ); + assertEquals( 0, parent.getChildren().size() ); + assertEquals( 1, parent.getVersion() ); // / hmmm, why is was version updated? + s.delete( parent ); + s.delete( child ); + s.getTransaction().commit(); + s.close(); + } + + protected void cleanupTest() throws Exception { + cleanup(); + super.cleanupTest(); + } + + private void cleanup() { + Session s = sfi().openSession(); + s.beginTransaction(); + + s.createQuery( "delete from VersionedNode where parent is not null" ).executeUpdate(); + s.createQuery( "delete from VersionedNode" ).executeUpdate(); + + s.getTransaction().commit(); + s.close(); + } + + protected void clearCounts() { + getSessions().getStatistics().clear(); + } + + protected void assertInsertCount(int expected) { + int inserts = ( int ) getSessions().getStatistics().getEntityInsertCount(); + assertEquals( "unexpected insert count", expected, inserts ); + } + + protected void assertUpdateCount(int expected) { + int updates = ( int ) getSessions().getStatistics().getEntityUpdateCount(); + assertEquals( "unexpected update counts", expected, updates ); + } + + protected void assertDeleteCount(int expected) { + int deletes = ( int ) getSessions().getStatistics().getEntityDeleteCount(); + assertEquals( "unexpected delete counts", expected, deletes ); + } +} diff --git a/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.hbm.xml b/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.hbm.xml new file mode 100644 index 0000000000..1be301eb70 --- /dev/null +++ b/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.hbm.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.java b/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.java new file mode 100644 index 0000000000..9eea0fa6cd --- /dev/null +++ b/testsuite/src/test/java/org/hibernate/test/readonly/VersionedNode.java @@ -0,0 +1,96 @@ +//$Id: $ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Middleware LLC. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + * + */ +package org.hibernate.test.readonly; + +import java.util.Set; +import java.util.HashSet; + +/** + * VersionedNode + * + * @author Gail Badner + */ +public class VersionedNode { + private String id; + private String name; + private long version; + + private VersionedNode parent; + private Set children = new HashSet(); + + public VersionedNode() { + } + + public VersionedNode(String id, String name) { + this.id = id; + this.name = name; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public long getVersion() { + return version; + } + + public void setVersion(long version) { + this.version = version; + } + + public VersionedNode getParent() { + return parent; + } + + public void setParent(VersionedNode parent) { + this.parent = parent; + } + + public Set getChildren() { + return children; + } + + public void setChildren(Set children) { + this.children = children; + } + + public void addChild(VersionedNode child) { + child.setParent( this ); + children.add( child ); + } +}