diff --git a/core/src/main/java/org/hibernate/engine/EntityEntry.java b/core/src/main/java/org/hibernate/engine/EntityEntry.java index 1c5b4ee819..b5d33ef1a2 100644 --- a/core/src/main/java/org/hibernate/engine/EntityEntry.java +++ b/core/src/main/java/org/hibernate/engine/EntityEntry.java @@ -46,6 +46,7 @@ public final class EntityEntry implements Serializable { private LockMode lockMode; private Status status; + private Status previousStatus; private final Serializable id; private Object[] loadedState; private Object[] deletedState; @@ -72,6 +73,7 @@ public final class EntityEntry implements Serializable { final boolean disableVersionIncrement, final boolean lazyPropertiesAreUnfetched) { this.status=status; + this.previousStatus = null; this.loadedState=loadedState; this.id=id; this.rowId=rowId; @@ -91,6 +93,7 @@ public final class EntityEntry implements Serializable { final Serializable id, final EntityMode entityMode, final Status status, + final Status previousStatus, final Object[] loadedState, final Object[] deletedState, final Object version, @@ -104,6 +107,7 @@ public final class EntityEntry implements Serializable { this.id = id; this.entityMode = entityMode; this.status = status; + this.previousStatus = previousStatus; this.loadedState = loadedState; this.deletedState = deletedState; this.version = version; @@ -130,7 +134,10 @@ public final class EntityEntry implements Serializable { if (status==Status.READ_ONLY) { loadedState = null; //memory optimization } - this.status = status; + if ( this.status != status ) { + this.previousStatus = this.status; + this.status = status; + } } public Serializable getId() { @@ -220,6 +227,7 @@ public final class EntityEntry implements Serializable { * exists in the database */ public void postDelete() { + previousStatus = status; status = Status.GONE; existsInDatabase = false; } @@ -246,18 +254,29 @@ public final class EntityEntry implements Serializable { return loadedState[propertyIndex]; } - public boolean requiresDirtyCheck(Object entity) { - - boolean isMutableInstance = - status != Status.READ_ONLY && - persister.isMutable(); - - return isMutableInstance && ( + public boolean requiresDirtyCheck(Object entity) { + return isModifiableEntity() && ( getPersister().hasMutableProperties() || !FieldInterceptionHelper.isInstrumented( entity ) || FieldInterceptionHelper.extractFieldInterceptor( entity).isDirty() ); - + } + + /** + * Can the entity be modified? + * + * The entity is modifiable if all of the following are true: + * + * @return true, if the entity is modifiable; false, otherwise, + */ + public boolean isModifiableEntity() { + return ( status != Status.READ_ONLY ) && + ! ( status == Status.DELETED && previousStatus == Status.READ_ONLY ) && + getPersister().isMutable(); } public void forceLocked(Object entity, Object nextVersion) { @@ -318,6 +337,7 @@ public final class EntityEntry implements Serializable { oos.writeObject( id ); oos.writeObject( entityMode.toString() ); oos.writeObject( status.toString() ); + oos.writeObject( ( previousStatus == null ? "" : previousStatus.toString() ) ); // todo : potentially look at optimizing these two arrays oos.writeObject( loadedState ); oos.writeObject( deletedState ); @@ -344,12 +364,17 @@ public final class EntityEntry implements Serializable { static EntityEntry deserialize( ObjectInputStream ois, SessionImplementor session) throws IOException, ClassNotFoundException { + String previousStatusString = null; return new EntityEntry( ( session == null ? null : session.getFactory() ), ( String ) ois.readObject(), ( Serializable ) ois.readObject(), EntityMode.parse( ( String ) ois.readObject() ), Status.parse( ( String ) ois.readObject() ), + ( ( previousStatusString = ( String ) ois.readObject() ).length() == 0 ? + null : + Status.parse( previousStatusString ) + ), ( Object[] ) ois.readObject(), ( Object[] ) ois.readObject(), ois.readObject(), diff --git a/core/src/main/java/org/hibernate/event/def/DefaultFlushEntityEventListener.java b/core/src/main/java/org/hibernate/event/def/DefaultFlushEntityEventListener.java index 8a60da2bb0..0845569ef1 100755 --- a/core/src/main/java/org/hibernate/event/def/DefaultFlushEntityEventListener.java +++ b/core/src/main/java/org/hibernate/event/def/DefaultFlushEntityEventListener.java @@ -38,7 +38,6 @@ import org.hibernate.classic.Validatable; import org.hibernate.engine.EntityEntry; import org.hibernate.engine.EntityKey; import org.hibernate.engine.Nullability; -import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.engine.SessionImplementor; import org.hibernate.engine.Status; import org.hibernate.engine.Versioning; @@ -254,10 +253,24 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener if ( log.isTraceEnabled() ) { if ( status == Status.DELETED ) { - log.trace( + if ( ! persister.isMutable() ) { + log.trace( + "Updating immutable, deleted entity: " + + MessageHelper.infoString( persister, entry.getId(), session.getFactory() ) + ); + } + else if ( ! entry.isModifiableEntity() ) { + log.trace( + "Updating non-modifiable, deleted entity: " + + MessageHelper.infoString( persister, entry.getId(), session.getFactory() ) + ); + } + else { + log.trace( "Updating deleted entity: " + MessageHelper.infoString( persister, entry.getId(), session.getFactory() ) ); + } } else { log.trace( @@ -303,7 +316,9 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener values, dirtyProperties, event.hasDirtyCollection(), - entry.getLoadedState(), + ( status == Status.DELETED && ! entry.isModifiableEntity() ? + persister.getPropertyValues( entity, entityMode ) : + entry.getLoadedState() ), entry.getVersion(), nextVersion, entity, @@ -447,7 +462,7 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener } private boolean hasDirtyCollections(FlushEntityEvent event, EntityPersister persister, Status status) { - if ( isCollectionDirtyCheckNecessary(persister, status) ) { + if ( isCollectionDirtyCheckNecessary(persister, status ) ) { DirtyCollectionSearchVisitor visitor = new DirtyCollectionSearchVisitor( event.getSession(), persister.getPropertyVersionability() @@ -463,8 +478,8 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener } private boolean isCollectionDirtyCheckNecessary(EntityPersister persister, Status status) { - return status==Status.MANAGED && - persister.isVersioned() && + return status==Status.MANAGED && + persister.isVersioned() && persister.hasCollections(); } @@ -503,7 +518,27 @@ public class DefaultFlushEntityEventListener implements FlushEntityEventListener if ( !cannotDirtyCheck ) { // dirty check against the usual snapshot of the entity dirtyProperties = persister.findDirty( values, loadedState, entity, session ); - + } + else if ( entry.getStatus() == Status.DELETED && ! event.getEntityEntry().isModifiableEntity() ) { + // A non-modifiable (e.g., read-only or immutable) entity needs to be have + // references to transient entities set to null before being deleted. No other + // fields should be updated. + if ( values != entry.getDeletedState() ) { + throw new IllegalStateException( + "Entity has status Status.DELETED but values != entry.getDeletedState" + ); + } + // Even if loadedState == null, we can dirty-check by comparing currentState and + // entry.getDeletedState() because the only fields to be updated are those that + // refer to transient entities that are being set to null. + // - currentState contains the entity's current property values. + // - entry.getDeletedState() contains the entity's current property values with + // references to transient entities set to null. + // - dirtyProperties will only contain properties that refer to transient entities + final Object[] currentState = + persister.getPropertyValues( event.getEntity(), event.getSession().getEntityMode() ); + dirtyProperties = persister.findDirty( entry.getDeletedState(), currentState, entity, session ); + cannotDirtyCheck = false; } else { // dirty check against the database snapshot, if possible/necessary diff --git a/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index e9a8e59ea4..f0a76d0d3c 100644 --- a/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -63,6 +63,7 @@ import org.hibernate.engine.LoadQueryInfluencers; import org.hibernate.engine.Mapping; import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.engine.SessionImplementor; +import org.hibernate.engine.Status; import org.hibernate.engine.ValueInclusion; import org.hibernate.engine.Versioning; import org.hibernate.exception.JDBCExceptionHelper; @@ -2751,8 +2752,21 @@ public abstract class AbstractEntityPersister final boolean[] propsToUpdate; final String[] updateStrings; - if ( entityMetamodel.isDynamicUpdate() && dirtyFields != null ) { - // For the case of dynamic-update="true", we need to generate the UPDATE SQL + EntityEntry entry = session.getPersistenceContext().getEntry( object ); + + // Ensure that an immutable or non-modifiable entity is not being updated unless it is + // in the process of being deleted. + if ( entry == null && ! isMutable() ) { + throw new IllegalStateException( "Updating immutable entity that is not in session yet!" ); + } + if ( entry != null && ! isModifiableEntity( entry ) && entry.getStatus() != Status.DELETED ) { + throw new IllegalStateException( "Updating non-modifiable entity that is not being deleted!" ); + } + if ( ( entityMetamodel.isDynamicUpdate() || ! isModifiableEntity( entry ) ) && dirtyFields != null ) { + // For the following cases we need to generate the UPDATE SQL + // - dynamic-update="true" + // - a non-modifiable entity (e.g., read-only or immutable) needs to have + // references to transient entities set to null before being deleted propsToUpdate = getPropertiesToUpdate( dirtyFields, hasDirtyCollection ); // don't need to check laziness (dirty checking algorithm handles that) updateStrings = new String[span]; @@ -3595,6 +3609,11 @@ public abstract class AbstractEntityPersister return entityMetamodel.isMutable(); } + private boolean isModifiableEntity(EntityEntry entry) { + + return ( entry == null ? isMutable() : entry.isModifiableEntity() ); + } + public boolean isAbstract() { return entityMetamodel.isAbstract(); } diff --git a/testsuite/src/test/java/org/hibernate/test/immutable/Contract.java b/testsuite/src/test/java/org/hibernate/test/immutable/Contract.java index 1cd4bc5411..9dbf034689 100755 --- a/testsuite/src/test/java/org/hibernate/test/immutable/Contract.java +++ b/testsuite/src/test/java/org/hibernate/test/immutable/Contract.java @@ -3,7 +3,10 @@ package org.hibernate.test.immutable; import java.io.Serializable; import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Set; public class Contract implements Serializable { @@ -11,15 +14,36 @@ public class Contract implements Serializable { private String customerName; private String type; private List variations; + private Contract parent; + private Set subcontracts; + private Set plans; + private Set parties; + private Set infos; public Contract() { super(); } - public Contract(String customerName, String type) { + public Contract(Plan plan, String customerName, String type) { + plans = new HashSet(); + plans.add( plan ); + if ( plan != null ) { + plan.getContracts().add( this ); + } this.customerName = customerName; this.type = type; variations = new ArrayList(); + subcontracts = new HashSet(); + parties = new HashSet(); + infos = new HashSet(); + } + + public Set getPlans() { + return plans; + } + + public void setPlans(Set plans) { + this.plans = plans; } public String getCustomerName() { @@ -54,4 +78,55 @@ public class Contract implements Serializable { this.variations = variations; } + public Contract getParent() { + return parent; + } + + public void setParent(Contract parent) { + this.parent = parent; + } + + public Set getSubcontracts() { + return subcontracts; + } + + public void setSubcontracts(Set subcontracts) { + this.subcontracts = subcontracts; + } + + public void addSubcontract(Contract subcontract) { + subcontracts.add( subcontract ); + subcontract.setParent( this ); + } + + public Set getParties() { + return parties; + } + + public void setParties(Set parties) { + this.parties = parties; + } + + public void addParty(Party party) { + parties.add( party ); + party.setContract( this ); + } + + public void removeParty(Party party) { + parties.remove( party ); + party.setContract( null ); + } + + public Set getInfos() { + return infos; + } + + public void setInfos(Set infos) { + this.infos = infos; + } + + public void addInfo(Info info) { + infos.add( info ); + info.setContract( this ); + } } diff --git a/testsuite/src/test/java/org/hibernate/test/immutable/ContractVariation.hbm.xml b/testsuite/src/test/java/org/hibernate/test/immutable/ContractVariation.hbm.xml index a117028b29..32a2c35d49 100755 --- a/testsuite/src/test/java/org/hibernate/test/immutable/ContractVariation.hbm.xml +++ b/testsuite/src/test/java/org/hibernate/test/immutable/ContractVariation.hbm.xml @@ -10,7 +10,34 @@ --> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -22,8 +49,27 @@ + + + + + + + + + + + + + + + + + + - + 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 f9ea84847f..5ef8e55f04 100755 --- a/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java +++ b/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java @@ -33,6 +33,8 @@ import org.hibernate.Hibernate; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.Transaction; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; import org.hibernate.criterion.Projections; import org.hibernate.junit.functional.FunctionalTestCase; import org.hibernate.junit.functional.FunctionalTestClassTestSuite; @@ -46,6 +48,11 @@ public class ImmutableTest extends FunctionalTestCase { super(str); } + public void configure(Configuration cfg) { + cfg.setProperty( Environment.GENERATE_STATISTICS, "true"); + cfg.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" ); + } + public String[] getMappings() { return new String[] { "immutable/ContractVariation.hbm.xml" }; } @@ -55,11 +62,14 @@ public class ImmutableTest extends FunctionalTestCase { } public void testPersistImmutable() { - Contract c = new Contract("gavin", "phone"); + Contract c = new Contract( null, "gavin", "phone"); ContractVariation cv1 = new ContractVariation(1, c); cv1.setText("expensive"); ContractVariation cv2 = new ContractVariation(2, c); cv2.setText("more expensive"); + + clearCounts(); + Session s = openSession(); Transaction t = s.beginTransaction(); s.persist(c); @@ -70,6 +80,10 @@ public class ImmutableTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 3 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); t = s.beginTransaction(); c = (Contract) s.createCriteria(Contract.class).uniqueResult(); @@ -90,10 +104,64 @@ public class ImmutableTest extends FunctionalTestCase { assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); t.commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 3 ); + } + + public void testPersistUpdateImmutableInSameTransaction() { + Contract c = new Contract( null, "gavin", "phone"); + ContractVariation cv1 = new ContractVariation(1, c); + cv1.setText("expensive"); + ContractVariation cv2 = new ContractVariation(2, c); + cv2.setText("more expensive"); + + clearCounts(); + + Session s = openSession(); + Transaction t = s.beginTransaction(); + s.persist(c); + // c, cv1, and cv2 were added to s by s.persist(c) (not hibernate), so they are modifiable + assertFalse( s.isReadOnly( c ) ); + assertFalse( s.isReadOnly( cv1 ) ); + assertFalse( s.isReadOnly( cv2 ) ); + c.setCustomerName( "gail" ); + t.commit(); + s.close(); + + assertInsertCount( 3 ); + assertUpdateCount( 0 ); + clearCounts(); + + s = openSession(); + t = s.beginTransaction(); + c = (Contract) s.createCriteria(Contract.class).uniqueResult(); + // c was loaded into s by hibernate, so it should be read-only + assertTrue( s.isReadOnly( c ) ); + 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" ); + // cv1 and cv2 were loaded into s by hibernate, so they should be read-only + assertTrue( s.isReadOnly( cv1 ) ); + assertTrue( s.isReadOnly( cv2 ) ); + s.delete(c); + assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); + assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); + t.commit(); + s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 3 ); } public void testSaveImmutable() { - Contract c = new Contract("gavin", "phone"); + clearCounts(); + + Contract c = new Contract( null, "gavin", "phone"); ContractVariation cv1 = new ContractVariation(1, c); cv1.setText("expensive"); ContractVariation cv2 = new ContractVariation(2, c); @@ -108,6 +176,10 @@ public class ImmutableTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 3 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); t = s.beginTransaction(); c = (Contract) s.createCriteria(Contract.class).uniqueResult(); @@ -128,10 +200,15 @@ public class ImmutableTest extends FunctionalTestCase { assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); t.commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 3 ); } public void testSaveOrUpdateImmutable() { - Contract c = new Contract("gavin", "phone"); + clearCounts(); + + Contract c = new Contract( null, "gavin", "phone"); ContractVariation cv1 = new ContractVariation(1, c); cv1.setText("expensive"); ContractVariation cv2 = new ContractVariation(2, c); @@ -146,6 +223,10 @@ public class ImmutableTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 3 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); t = s.beginTransaction(); c = (Contract) s.createCriteria(Contract.class).uniqueResult(); @@ -166,10 +247,15 @@ public class ImmutableTest extends FunctionalTestCase { assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); t.commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 3 ); } public void testImmutable() { - Contract c = new Contract("gavin", "phone"); + clearCounts(); + + Contract c = new Contract( null, "gavin", "phone"); ContractVariation cv1 = new ContractVariation(1, c); cv1.setText("expensive"); ContractVariation cv2 = new ContractVariation(2, c); @@ -183,7 +269,11 @@ public class ImmutableTest extends FunctionalTestCase { assertFalse( s.isReadOnly( cv2 ) ); t.commit(); s.close(); - + + assertInsertCount( 3 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); t = s.beginTransaction(); c = (Contract) s.createCriteria(Contract.class).uniqueResult(); @@ -201,7 +291,11 @@ public class ImmutableTest extends FunctionalTestCase { assertTrue( s.isReadOnly( cv1 ) ); assertFalse( s.contains( cv2 ) ); s.close(); - + + assertInsertCount( 0 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); t = s.beginTransaction(); c = (Contract) s.createCriteria(Contract.class).uniqueResult(); @@ -222,10 +316,15 @@ public class ImmutableTest extends FunctionalTestCase { assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); t.commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 3 ); } public void testPersistAndUpdateImmutable() { - Contract c = new Contract("gavin", "phone"); + clearCounts(); + + Contract c = new Contract( null, "gavin", "phone"); ContractVariation cv1 = new ContractVariation(1, c); cv1.setText("expensive"); ContractVariation cv2 = new ContractVariation(2, c); @@ -241,6 +340,10 @@ public class ImmutableTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 3 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); t = s.beginTransaction(); c = (Contract) s.createCriteria(Contract.class).uniqueResult(); @@ -259,6 +362,10 @@ public class ImmutableTest extends FunctionalTestCase { assertFalse( s.contains( cv2 ) ); s.close(); + assertInsertCount( 0 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); t = s.beginTransaction(); c = (Contract) s.createCriteria(Contract.class).uniqueResult(); @@ -279,10 +386,15 @@ public class ImmutableTest extends FunctionalTestCase { assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); t.commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 3 ); } public void testUpdateAndDeleteManagedImmutable() { - Contract c = new Contract("gavin", "phone"); + clearCounts(); + + Contract c = new Contract( null, "gavin", "phone"); ContractVariation cv1 = new ContractVariation(1, c); cv1.setText("expensive"); ContractVariation cv2 = new ContractVariation(2, c); @@ -293,6 +405,10 @@ public class ImmutableTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 3 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); t = s.beginTransaction(); c = (Contract) s.createCriteria(Contract.class).uniqueResult(); @@ -314,10 +430,15 @@ public class ImmutableTest extends FunctionalTestCase { assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); t.commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 3 ); } public void testGetAndDeleteManagedImmutable() { - Contract c = new Contract("gavin", "phone"); + clearCounts(); + + Contract c = new Contract( null, "gavin", "phone"); ContractVariation cv1 = new ContractVariation(1, c); cv1.setText("expensive"); ContractVariation cv2 = new ContractVariation(2, c); @@ -328,6 +449,10 @@ public class ImmutableTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 3 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); t = s.beginTransaction(); c = (Contract) s.get( Contract.class, c.getId() ); @@ -349,10 +474,15 @@ public class ImmutableTest extends FunctionalTestCase { assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); t.commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 3 ); } public void testDeleteDetachedImmutable() { - Contract c = new Contract("gavin", "phone"); + clearCounts(); + + Contract c = new Contract( null, "gavin", "phone"); ContractVariation cv1 = new ContractVariation(1, c); cv1.setText("expensive"); ContractVariation cv2 = new ContractVariation(2, c); @@ -363,33 +493,26 @@ public class ImmutableTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 3 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); t = s.beginTransaction(); s.delete( c ); - /* c = (Contract) s.createCriteria(Contract.class).uniqueResult(); - // c was loaded into s by hibernate, so it should be read-only - assertTrue( s.isReadOnly( c ) ); - 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" ); - // cv1 and cv2 were loaded into s by hibernate, so they should be read-only - assertTrue( s.isReadOnly( cv1 ) ); - assertTrue( s.isReadOnly( cv2 ) ); - s.delete(c); - assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); - assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); - */ + assertNull( c ); t.commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 3 ); } public void testDeleteDetachedModifiedImmutable() { - Contract c = new Contract("gavin", "phone"); + clearCounts(); + + Contract c = new Contract( null, "gavin", "phone"); ContractVariation cv1 = new ContractVariation(1, c); cv1.setText("expensive"); ContractVariation cv2 = new ContractVariation(2, c); @@ -400,35 +523,26 @@ public class ImmutableTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 3 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); t = s.beginTransaction(); c.setCustomerName( "sherman" ); s.delete( c ); - /* - c = (Contract) s.createCriteria(Contract.class).uniqueResult(); - // c was loaded into s by hibernate, so it should be read-only - assertTrue( s.isReadOnly( c ) ); - 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" ); - // cv1 and cv2 were loaded into s by hibernate, so they should be read-only - assertTrue( s.isReadOnly( cv1 ) ); - assertTrue( s.isReadOnly( cv2 ) ); - s.delete(c); - assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); - assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); - */ t.commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 3 ); } public void testImmutableParentEntityWithUpdate() { - Contract c = new Contract("gavin", "phone"); + clearCounts(); + + Contract c = new Contract( null, "gavin", "phone"); ContractVariation cv1 = new ContractVariation(1, c); cv1.setText("expensive"); ContractVariation cv2 = new ContractVariation(2, c); @@ -439,6 +553,10 @@ public class ImmutableTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 3 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); t = s.beginTransaction(); c.setCustomerName("foo bar"); @@ -454,6 +572,8 @@ public class ImmutableTest extends FunctionalTestCase { assertFalse( s.isReadOnly( cv2 ) ); s.close(); + assertUpdateCount( 0 ); + s = openSession(); t = s.beginTransaction(); c = (Contract) s.createCriteria(Contract.class).uniqueResult(); @@ -469,10 +589,15 @@ public class ImmutableTest extends FunctionalTestCase { assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); t.commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 3 ); } public void testImmutableChildEntityWithUpdate() { - Contract c = new Contract("gavin", "phone"); + clearCounts(); + + Contract c = new Contract( null, "gavin", "phone"); ContractVariation cv1 = new ContractVariation(1, c); cv1.setText("expensive"); ContractVariation cv2 = new ContractVariation(2, c); @@ -483,6 +608,10 @@ public class ImmutableTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 3 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); t = s.beginTransaction(); cv1 = (ContractVariation) c.getVariations().iterator().next(); @@ -498,6 +627,8 @@ public class ImmutableTest extends FunctionalTestCase { assertFalse( s.isReadOnly( cv2 ) ); s.close(); + assertUpdateCount( 0 ); + s = openSession(); t = s.beginTransaction(); c = (Contract) s.createCriteria(Contract.class).uniqueResult(); @@ -513,10 +644,15 @@ public class ImmutableTest extends FunctionalTestCase { assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); t.commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 3 ); } public void testImmutableCollectionWithUpdate() { - Contract c = new Contract("gavin", "phone"); + clearCounts(); + + Contract c = new Contract( null, "gavin", "phone"); ContractVariation cv1 = new ContractVariation(1, c); cv1.setText("expensive"); ContractVariation cv2 = new ContractVariation(2, c); @@ -527,6 +663,9 @@ public class ImmutableTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 3 ); + assertUpdateCount( 0 ); + s = openSession(); t = s.beginTransaction(); c.getVariations().add( new ContractVariation(3, c) ); @@ -542,6 +681,8 @@ public class ImmutableTest extends FunctionalTestCase { s.close(); } + assertUpdateCount( 0 ); + s = openSession(); t = s.beginTransaction(); c = (Contract) s.createCriteria(Contract.class).uniqueResult(); @@ -557,10 +698,15 @@ public class ImmutableTest extends FunctionalTestCase { assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); t.commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 3 ); } - public void testImmutableParentEntityWithMerge() { - Contract c = new Contract("gavin", "phone"); + public void testUnmodifiedImmutableParentEntityWithMerge() { + clearCounts(); + + Contract c = new Contract( null, "gavin", "phone"); ContractVariation cv1 = new ContractVariation(1, c); cv1.setText("expensive"); ContractVariation cv2 = new ContractVariation(2, c); @@ -571,6 +717,65 @@ public class ImmutableTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 3 ); + assertUpdateCount( 0 ); + clearCounts(); + + s = openSession(); + t = s.beginTransaction(); + c = ( Contract ) s.merge( c ); + // c was loaded into s by hibernate in the merge process, so it is read-only + assertTrue( s.isReadOnly( c ) ); + assertTrue( Hibernate.isInitialized( c.getVariations() ) ); + Iterator it = c.getVariations().iterator(); + cv1 = (ContractVariation) it.next(); + cv2 = (ContractVariation) it.next(); + // cv1 and cv2 were loaded into s by hibernate in the merge process, so they are read-only + assertTrue( s.isReadOnly( cv1 ) ); + assertTrue( s.isReadOnly( cv2 ) ); + t.commit(); + s.close(); + + assertUpdateCount( 0 ); + + s = openSession(); + t = s.beginTransaction(); + c = (Contract) s.createCriteria(Contract.class).uniqueResult(); + assertEquals( c.getCustomerName(), "gavin" ); + assertEquals( c.getVariations().size(), 2 ); + 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 Long(0) ); + assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); + t.commit(); + s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 3 ); + } + + public void testImmutableParentEntityWithMerge() { + clearCounts(); + + Contract c = new Contract( null, "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(); + + assertInsertCount( 3 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); t = s.beginTransaction(); c.setCustomerName("foo bar"); @@ -587,6 +792,8 @@ public class ImmutableTest extends FunctionalTestCase { t.commit(); s.close(); + assertUpdateCount( 0 ); + s = openSession(); t = s.beginTransaction(); c = (Contract) s.createCriteria(Contract.class).uniqueResult(); @@ -602,10 +809,16 @@ public class ImmutableTest extends FunctionalTestCase { assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); t.commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 3 ); + } public void testImmutableChildEntityWithMerge() { - Contract c = new Contract("gavin", "phone"); + clearCounts(); + + Contract c = new Contract( null, "gavin", "phone"); ContractVariation cv1 = new ContractVariation(1, c); cv1.setText("expensive"); ContractVariation cv2 = new ContractVariation(2, c); @@ -616,6 +829,10 @@ public class ImmutableTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 3 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); t = s.beginTransaction(); cv1 = (ContractVariation) c.getVariations().iterator().next(); @@ -633,6 +850,8 @@ public class ImmutableTest extends FunctionalTestCase { t.commit(); s.close(); + assertUpdateCount( 0 ); + s = openSession(); t = s.beginTransaction(); c = (Contract) s.createCriteria(Contract.class).uniqueResult(); @@ -648,10 +867,15 @@ public class ImmutableTest extends FunctionalTestCase { assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); t.commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 3 ); } public void testImmutableCollectionWithMerge() { - Contract c = new Contract("gavin", "phone"); + clearCounts(); + + Contract c = new Contract( null, "gavin", "phone"); ContractVariation cv1 = new ContractVariation(1, c); cv1.setText("expensive"); ContractVariation cv2 = new ContractVariation(2, c); @@ -662,6 +886,11 @@ public class ImmutableTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 3 ); + assertUpdateCount( 0 ); + + clearCounts(); + s = openSession(); t = s.beginTransaction(); c.getVariations().add( new ContractVariation(3, c) ); @@ -693,6 +922,28 @@ public class ImmutableTest extends FunctionalTestCase { assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); t.commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 3 ); + } + + 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/immutable/Info.java b/testsuite/src/test/java/org/hibernate/test/immutable/Info.java new file mode 100644 index 0000000000..911a2b94bc --- /dev/null +++ b/testsuite/src/test/java/org/hibernate/test/immutable/Info.java @@ -0,0 +1,43 @@ +//$Id: Contract.java 7222 2005-06-19 17:22:01Z oneovthafew $ +package org.hibernate.test.immutable; + +import java.io.Serializable; + +public class Info implements Serializable { + + private long id; + private String text; + private Contract contract; + + public Info() { + super(); + } + + public Info(String text) { + this.text = text; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public Contract getContract() { + return contract; + } + + public void setContract(Contract contract ) { + this.contract = contract; + } +} \ No newline at end of file diff --git a/testsuite/src/test/java/org/hibernate/test/immutable/Party.java b/testsuite/src/test/java/org/hibernate/test/immutable/Party.java new file mode 100644 index 0000000000..49d2740550 --- /dev/null +++ b/testsuite/src/test/java/org/hibernate/test/immutable/Party.java @@ -0,0 +1,47 @@ +//$Id: Contract.java 7222 2005-06-19 17:22:01Z oneovthafew $ +package org.hibernate.test.immutable; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class Party implements Serializable { + + private long id; + private Contract contract; + private String name; + + public Party() { + super(); + } + + public Party(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public Contract getContract() { + return contract; + } + + public void setContract(Contract contract) { + this.contract = contract; + } +} \ No newline at end of file diff --git a/testsuite/src/test/java/org/hibernate/test/immutable/Plan.java b/testsuite/src/test/java/org/hibernate/test/immutable/Plan.java new file mode 100644 index 0000000000..7810f6b9d1 --- /dev/null +++ b/testsuite/src/test/java/org/hibernate/test/immutable/Plan.java @@ -0,0 +1,80 @@ +//$Id: ContractVariation.java 7222 2005-06-19 17:22:01Z oneovthafew $ +package org.hibernate.test.immutable; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +public class Plan implements Serializable { + + private long id; + private String description; + private Set contracts; + + public Plan() { + this( null ); + } + + public Plan(String description) { + this.description = description; + contracts = new HashSet(); + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Set getContracts() { + return contracts; + } + + public void setContracts(Set contracts) { + this.contracts = contracts; + } + + public void addContract(Contract contract) { + if ( ! contracts.add( contract ) ) { + return; + } + if ( contract.getParent() != null ) { + addContract( contract.getParent() ); + } + contract.getPlans().add( this ); + for ( Iterator it=contract.getSubcontracts().iterator(); it.hasNext(); ) { + Contract sub = ( Contract ) it.next(); + addContract( sub ); + } + } + + public void removeContract(Contract contract) { + if ( contract.getParent() != null ) { + contract.getParent().getSubcontracts().remove( contract ); + contract.setParent( null ); + } + removeSubcontracts( contract ); + contract.getPlans().remove( this ); + contracts.remove( contract ); + } + + public void removeSubcontracts(Contract contract) { + for ( Iterator it=contract.getSubcontracts().iterator(); it.hasNext(); ) { + Contract sub = ( Contract ) it.next(); + removeSubcontracts( sub ); + sub.getPlans().remove( this ); + contracts.remove( sub ); + } + } +} \ No newline at end of file diff --git a/testsuite/src/test/java/org/hibernate/test/readonly/AbstractReadOnlyTest.java b/testsuite/src/test/java/org/hibernate/test/readonly/AbstractReadOnlyTest.java new file mode 100644 index 0000000000..a48089ee92 --- /dev/null +++ b/testsuite/src/test/java/org/hibernate/test/readonly/AbstractReadOnlyTest.java @@ -0,0 +1,79 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, 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 org.hibernate.CacheMode; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; +import org.hibernate.junit.functional.FunctionalTestCase; + +/** + * + * @author Gail Badner + */ +public abstract class AbstractReadOnlyTest extends FunctionalTestCase { + + public AbstractReadOnlyTest(String str) { + super(str); + } + + public void configure(Configuration cfg) { + cfg.setProperty( Environment.GENERATE_STATISTICS, "true"); + cfg.setProperty( Environment.STATEMENT_BATCH_SIZE, "0" ); + } + + public org.hibernate.classic.Session openSession() { + org.hibernate.classic.Session s = super.openSession(); + s.setCacheMode( getSessionCacheMode() ); + return s; + } + + protected CacheMode getSessionCacheMode() { + return CacheMode.IGNORE; + } + + public String getCacheConcurrencyStrategy() { + return null; + } + + 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 ); + } +} \ No newline at end of file diff --git a/testsuite/src/test/java/org/hibernate/test/readonly/criteria/Course.java b/testsuite/src/test/java/org/hibernate/test/readonly/Course.java similarity index 90% rename from testsuite/src/test/java/org/hibernate/test/readonly/criteria/Course.java rename to testsuite/src/test/java/org/hibernate/test/readonly/Course.java index 3a83671346..b296747847 100755 --- a/testsuite/src/test/java/org/hibernate/test/readonly/criteria/Course.java +++ b/testsuite/src/test/java/org/hibernate/test/readonly/Course.java @@ -1,5 +1,5 @@ //$Id: Course.java 5686 2005-02-12 07:27:32Z steveebersole $ -package org.hibernate.test.readonly.criteria; +package org.hibernate.test.readonly; /** * @author Gavin King diff --git a/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.hbm.xml b/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.hbm.xml index 359824df98..9c0d4f7330 100755 --- a/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.hbm.xml +++ b/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.hbm.xml @@ -13,10 +13,10 @@ - + - + diff --git a/testsuite/src/test/java/org/hibernate/test/readonly/criteria/Enrolment.hbm.xml b/testsuite/src/test/java/org/hibernate/test/readonly/Enrolment.hbm.xml similarity index 94% rename from testsuite/src/test/java/org/hibernate/test/readonly/criteria/Enrolment.hbm.xml rename to testsuite/src/test/java/org/hibernate/test/readonly/Enrolment.hbm.xml index d2115b3f2a..83c7db5294 100755 --- a/testsuite/src/test/java/org/hibernate/test/readonly/criteria/Enrolment.hbm.xml +++ b/testsuite/src/test/java/org/hibernate/test/readonly/Enrolment.hbm.xml @@ -3,7 +3,7 @@ "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> - + diff --git a/testsuite/src/test/java/org/hibernate/test/readonly/criteria/Enrolment.java b/testsuite/src/test/java/org/hibernate/test/readonly/Enrolment.java similarity index 96% rename from testsuite/src/test/java/org/hibernate/test/readonly/criteria/Enrolment.java rename to testsuite/src/test/java/org/hibernate/test/readonly/Enrolment.java index d48df67d3d..75469401fd 100755 --- a/testsuite/src/test/java/org/hibernate/test/readonly/criteria/Enrolment.java +++ b/testsuite/src/test/java/org/hibernate/test/readonly/Enrolment.java @@ -1,5 +1,5 @@ //$Id: Enrolment.java 6970 2005-05-31 20:24:41Z oneovthafew $ -package org.hibernate.test.readonly.criteria; +package org.hibernate.test.readonly; import java.io.Serializable; diff --git a/testsuite/src/test/java/org/hibernate/test/readonly/criteria/ReadOnlyCriteriaQueryTest.java b/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyCriteriaQueryTest.java similarity index 99% rename from testsuite/src/test/java/org/hibernate/test/readonly/criteria/ReadOnlyCriteriaQueryTest.java rename to testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyCriteriaQueryTest.java index 1a0ab8a966..8d0451993e 100755 --- a/testsuite/src/test/java/org/hibernate/test/readonly/criteria/ReadOnlyCriteriaQueryTest.java +++ b/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyCriteriaQueryTest.java @@ -1,5 +1,5 @@ //$Id: CriteriaQueryTest.java 10976 2006-12-12 23:22:26Z steve.ebersole@jboss.com $ -package org.hibernate.test.readonly.criteria; +package org.hibernate.test.readonly; import java.util.Iterator; import java.util.List; @@ -7,6 +7,7 @@ import java.util.Map; import junit.framework.Test; +import org.hibernate.CacheMode; import org.hibernate.Criteria; import org.hibernate.FetchMode; import org.hibernate.Hibernate; @@ -30,6 +31,7 @@ import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; import org.hibernate.test.hql.Animal; import org.hibernate.test.hql.Reptile; +import org.hibernate.test.readonly.AbstractReadOnlyTest; import org.hibernate.transform.Transformers; import org.hibernate.type.Type; import org.hibernate.util.SerializationHelper; @@ -37,14 +39,14 @@ import org.hibernate.util.SerializationHelper; /** * @author Gail Badner (adapted from org.hibernate.test.criteria.CriteriaQueryTest by Gavin King) */ -public class ReadOnlyCriteriaQueryTest extends FunctionalTestCase { +public class ReadOnlyCriteriaQueryTest extends AbstractReadOnlyTest { public ReadOnlyCriteriaQueryTest(String str) { super(str); } public String[] getMappings() { - return new String[] { "readonly/criteria/Enrolment.hbm.xml" }; + return new String[] { "readonly/Enrolment.hbm.xml" }; } public void configure(Configuration cfg) { @@ -54,7 +56,7 @@ public class ReadOnlyCriteriaQueryTest extends FunctionalTestCase { cfg.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "true" ); cfg.setProperty( Environment.GENERATE_STATISTICS, "true" ); } - + public static Test suite() { return new FunctionalTestClassTestSuite( ReadOnlyCriteriaQueryTest.class ); } @@ -92,6 +94,10 @@ public class ReadOnlyCriteriaQueryTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 4 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); t = s.beginTransaction(); Criteria criteria = s.createCriteria( Student.class ); @@ -125,6 +131,9 @@ public class ReadOnlyCriteriaQueryTest extends FunctionalTestCase { s.delete(enrolment); t.commit(); s.close(); + + assertUpdateCount( 1 ); + assertDeleteCount( 4 ); } public void testModifiableSessionReadOnlyCriteria() { diff --git a/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyProxyTest.java b/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyProxyTest.java index 328e897baf..7e6f549e5f 100644 --- a/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyProxyTest.java +++ b/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyProxyTest.java @@ -52,7 +52,7 @@ import org.hibernate.junit.functional.FunctionalTestClassTestSuite; * * @author Gail Badner */ -public class ReadOnlyProxyTest extends FunctionalTestCase { +public class ReadOnlyProxyTest extends AbstractReadOnlyTest { public ReadOnlyProxyTest(String str) { super(str); @@ -62,14 +62,6 @@ public class ReadOnlyProxyTest extends FunctionalTestCase { return new String[] { "readonly/DataPoint.hbm.xml", "readonly/TextHolder.hbm.xml" }; } - public void configure(Configuration cfg) { - cfg.setProperty(Environment.STATEMENT_BATCH_SIZE, "20"); - } - - public String getCacheConcurrencyStrategy() { - return null; - } - public static Test suite() { return new FunctionalTestClassTestSuite( ReadOnlyProxyTest.class ); } diff --git a/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlySessionLazyNonLazyTest.java b/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlySessionLazyNonLazyTest.java index 1bddb921e5..8b9a90fa66 100644 --- a/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlySessionLazyNonLazyTest.java +++ b/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlySessionLazyNonLazyTest.java @@ -66,7 +66,7 @@ import org.hibernate.junit.functional.FunctionalTestClassTestSuite; * * @author Gail Badner */ -public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase { +public class ReadOnlySessionLazyNonLazyTest extends AbstractReadOnlyTest { public ReadOnlySessionLazyNonLazyTest(String str) { super(str); @@ -76,14 +76,6 @@ public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase { return new String[] { "readonly/DataPoint.hbm.xml" }; } - public void configure(Configuration cfg) { - cfg.setProperty(Environment.STATEMENT_BATCH_SIZE, "20"); - } - - public String getCacheConcurrencyStrategy() { - return null; - } - public static Test suite() { return new FunctionalTestClassTestSuite( ReadOnlySessionLazyNonLazyTest.class ); } @@ -106,7 +98,6 @@ public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase { Session s = openSession(); assertFalse( s.isDefaultReadOnly() ); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); s.save( cOrig ); checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s ); @@ -195,7 +186,6 @@ public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase { Set expectedReadOnlyObjects = new HashSet(); Session s = openSession(); assertFalse( s.isDefaultReadOnly() ); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); s.save( cOrig ); checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s ); @@ -278,7 +268,6 @@ public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase { DataPoint lazyDataPointOrig = ( DataPoint ) cOrig.getLazyDataPoints().iterator().next(); Session s = openSession(); assertFalse( s.isDefaultReadOnly() ); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); s.save( cOrig ); checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s ); @@ -362,7 +351,6 @@ public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase { DataPoint lazyDataPointOrig = ( DataPoint ) cOrig.getLazyDataPoints().iterator().next(); Session s = openSession(); assertFalse( s.isDefaultReadOnly() ); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); s.save( cOrig ); checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s ); @@ -449,7 +437,6 @@ public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase { DataPoint lazyDataPointOrig = ( DataPoint ) cOrig.getLazyDataPoints().iterator().next(); Session s = openSession(); assertFalse( s.isDefaultReadOnly() ); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); s.save( cOrig ); checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s ); @@ -533,7 +520,6 @@ public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase { DataPoint lazyDataPointOrig = ( DataPoint ) cOrig.getLazyDataPoints().iterator().next(); Session s = openSession(); assertFalse( s.isDefaultReadOnly() ); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); s.save( cOrig ); checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s ); @@ -618,7 +604,6 @@ public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase { Session s = openSession(); assertFalse( s.isDefaultReadOnly() ); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); s.save( cOrig ); checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s ); @@ -697,7 +682,6 @@ public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase { Session s = openSession(); assertFalse( s.isDefaultReadOnly() ); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); s.save( cOrig ); checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s ); @@ -767,7 +751,6 @@ public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase { Session s = openSession(); assertFalse( s.isDefaultReadOnly() ); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); s.save( cOrig ); checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s ); @@ -847,7 +830,6 @@ public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase { Session s = openSession(); assertFalse( s.isDefaultReadOnly() ); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); s.save( cOrig ); checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s ); @@ -916,7 +898,6 @@ public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase { Session s = openSession(); assertFalse( s.isDefaultReadOnly() ); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); s.save( cOrig ); checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s ); @@ -961,7 +942,6 @@ public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase { Session s = openSession(); assertFalse( s.isDefaultReadOnly() ); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); s.save( cOrig ); checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s ); @@ -1045,7 +1025,6 @@ public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase { Session s = openSession(); assertFalse( s.isDefaultReadOnly() ); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); s.save( cOrig ); checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s ); @@ -1118,7 +1097,6 @@ public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase { Session s = openSession(); assertFalse( s.isDefaultReadOnly() ); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); s.save( cOrig ); checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s ); @@ -1199,7 +1177,6 @@ public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase { Session s = openSession(); assertFalse( s.isDefaultReadOnly() ); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); s.save( cOrig ); checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s ); @@ -1257,14 +1234,12 @@ public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase { Container cOrig = createContainer(); Session s = openSession(); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); s.save( cOrig ); t.commit(); s.close(); s = openSession(); - s.setCacheMode(CacheMode.IGNORE); t = s.beginTransaction(); List list = s.createQuery( "from Container c left outer join c.nonLazyInfo where c.id = :id" ) .setLong( "id", cOrig.getId() ) @@ -1301,14 +1276,12 @@ public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase { Container cOrig = createContainer(); Session s = openSession(); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); s.save( cOrig ); t.commit(); s.close(); s = openSession(); - s.setCacheMode(CacheMode.IGNORE); t = s.beginTransaction(); List list = s.createQuery( "from Container c left join fetch c.nonLazyInfo where c.id = :id" ) .setLong( "id", cOrig.getId() ) @@ -1348,14 +1321,12 @@ public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase { Container cOrig = createContainer(); Session s = openSession(); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); s.save( cOrig ); t.commit(); s.close(); s = openSession(); - s.setCacheMode(CacheMode.IGNORE); t = s.beginTransaction(); Container c = ( Container ) s.get( Container.class, cOrig.getId() ); assertNotNull( c ); diff --git a/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlySessionTest.java b/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlySessionTest.java index 79648bade9..7c44bc77a3 100644 --- a/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlySessionTest.java +++ b/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlySessionTest.java @@ -48,7 +48,7 @@ import org.hibernate.proxy.HibernateProxy; * * @author Gail Badner */ -public class ReadOnlySessionTest extends FunctionalTestCase { +public class ReadOnlySessionTest extends AbstractReadOnlyTest { public ReadOnlySessionTest(String str) { super(str); @@ -58,14 +58,6 @@ public class ReadOnlySessionTest extends FunctionalTestCase { return new String[] { "readonly/DataPoint.hbm.xml", "readonly/TextHolder.hbm.xml" }; } - public void configure(Configuration cfg) { - cfg.setProperty(Environment.STATEMENT_BATCH_SIZE, "20"); - } - - public String getCacheConcurrencyStrategy() { - return null; - } - public static Test suite() { return new FunctionalTestClassTestSuite( ReadOnlySessionTest.class ); } 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 f83f13f2d8..4cb0e05560 100755 --- a/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java +++ b/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java @@ -47,7 +47,7 @@ import org.hibernate.junit.functional.FunctionalTestClassTestSuite; * @author Gavin King * @author Gail Badner */ -public class ReadOnlyTest extends FunctionalTestCase { +public class ReadOnlyTest extends AbstractReadOnlyTest { public ReadOnlyTest(String str) { super(str); @@ -57,21 +57,14 @@ public class ReadOnlyTest extends FunctionalTestCase { return new String[] { "readonly/DataPoint.hbm.xml", "readonly/TextHolder.hbm.xml" }; } - public void configure(Configuration cfg) { - cfg.setProperty(Environment.STATEMENT_BATCH_SIZE, "20"); - } - - public String getCacheConcurrencyStrategy() { - return null; - } - public static Test suite() { return new FunctionalTestClassTestSuite( ReadOnlyTest.class ); } public void testReadOnlyOnProxies() { + clearCounts(); + Session s = openSession(); - s.setCacheMode( CacheMode.IGNORE ); s.beginTransaction(); DataPoint dp = new DataPoint(); dp.setX( new BigDecimal( 0.1d ).setScale(19, BigDecimal.ROUND_DOWN) ); @@ -82,8 +75,11 @@ public class ReadOnlyTest extends FunctionalTestCase { s.getTransaction().commit(); s.close(); + assertInsertCount( 1 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); - s.setCacheMode(CacheMode.IGNORE); s.beginTransaction(); dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpId ) ); assertFalse( "was initialized", Hibernate.isInitialized( dp ) ); @@ -96,20 +92,27 @@ public class ReadOnlyTest extends FunctionalTestCase { s.getTransaction().commit(); s.close(); + assertUpdateCount( 0 ); + s = openSession(); s.beginTransaction(); List list = s.createQuery( "from DataPoint where description = 'changed'" ).list(); assertEquals( "change written to database", 0, list.size() ); - s.createQuery("delete from DataPoint").executeUpdate(); + assertEquals( 1, s.createQuery("delete from DataPoint").executeUpdate() ); s.getTransaction().commit(); s.close(); + + assertUpdateCount( 0 ); + //deletes from Query.executeUpdate() are not tracked + //assertDeleteCount( 1 ); } public void testReadOnlyMode() { - + + clearCounts(); + Session s = openSession(); - s.setCacheMode(CacheMode.IGNORE); - Transaction t = s.beginTransaction(); + Transaction t = s.beginTransaction(); for ( int i=0; i<100; i++ ) { DataPoint dp = new DataPoint(); dp.setX( new BigDecimal(i * 0.1d).setScale(19, BigDecimal.ROUND_DOWN) ); @@ -118,9 +121,12 @@ public class ReadOnlyTest extends FunctionalTestCase { } t.commit(); s.close(); - + + assertInsertCount( 100 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); - s.setCacheMode(CacheMode.IGNORE); t = s.beginTransaction(); int i = 0; ScrollableResults sr = s.createQuery("from DataPoint dp order by dp.x asc") @@ -134,20 +140,27 @@ public class ReadOnlyTest extends FunctionalTestCase { dp.setDescription("done!"); } t.commit(); + + assertUpdateCount( 1 ); + clearCounts(); + s.clear(); t = s.beginTransaction(); List single = s.createQuery("from DataPoint where description='done!'").list(); assertEquals( single.size(), 1 ); - s.createQuery("delete from DataPoint").executeUpdate(); + assertEquals( 100, s.createQuery("delete from DataPoint").executeUpdate() ); t.commit(); s.close(); - + + assertUpdateCount( 0 ); + //deletes from Query.executeUpdate() are not tracked + //assertDeleteCount( 100 ); } public void testReadOnlyModeAutoFlushOnQuery() { + clearCounts(); Session s = openSession(); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); DataPoint dpFirst = null; for ( int i=0; i<100; i++ ) { @@ -156,9 +169,18 @@ public class ReadOnlyTest extends FunctionalTestCase { dp.setY( new BigDecimal( Math.cos( dp.getX().doubleValue() ) ).setScale(19, BigDecimal.ROUND_DOWN) ); s.save(dp); } + + assertInsertCount( 0 ); + assertUpdateCount( 0 ); + ScrollableResults sr = s.createQuery("from DataPoint dp order by dp.x asc") .setReadOnly(true) .scroll(ScrollMode.FORWARD_ONLY); + + assertInsertCount( 100 ); + assertUpdateCount( 0 ); + clearCounts(); + while ( sr.next() ) { DataPoint dp = (DataPoint) sr.get(0); assertFalse( s.isReadOnly( dp ) ); @@ -167,23 +189,30 @@ public class ReadOnlyTest extends FunctionalTestCase { t.commit(); s.close(); + assertUpdateCount( 0 ); + assertDeleteCount( 100 ); } - public void testReadOnlyRefresh() { + public void testSaveReadOnlyModifyInSaveTransaction() { + clearCounts(); Session s = openSession(); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); DataPoint dp = new DataPoint(); dp.setDescription( "original" ); 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); + s.setReadOnly( dp, true ); + dp.setDescription( "different" ); t.commit(); s.close(); + assertInsertCount( 1 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); - s.setCacheMode(CacheMode.IGNORE); t = s.beginTransaction(); dp = ( DataPoint ) s.get( DataPoint.class, dp.getId() ); s.setReadOnly( dp, true ); @@ -196,6 +225,9 @@ public class ReadOnlyTest extends FunctionalTestCase { assertEquals( "changed", dp.getDescription() ); t.commit(); + assertInsertCount( 0 ); + assertUpdateCount( 0 ); + s.clear(); t = s.beginTransaction(); dp = ( DataPoint ) s.get( DataPoint.class, dp.getId() ); @@ -204,12 +236,15 @@ public class ReadOnlyTest extends FunctionalTestCase { t.commit(); s.close(); + assertUpdateCount( 0 ); + assertDeleteCount( 1 ); + clearCounts(); } - public void testReadOnlyRefreshDetached() { + public void testReadOnlyRefresh() { + clearCounts(); Session s = openSession(); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); DataPoint dp = new DataPoint(); dp.setDescription( "original" ); @@ -219,8 +254,57 @@ public class ReadOnlyTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 1 ); + assertUpdateCount( 0 ); + clearCounts(); + + s = openSession(); + t = s.beginTransaction(); + dp = ( DataPoint ) s.get( DataPoint.class, dp.getId() ); + s.setReadOnly( dp, true ); + assertEquals( "original", dp.getDescription() ); + dp.setDescription( "changed" ); + assertEquals( "changed", dp.getDescription() ); + s.refresh( dp ); + assertEquals( "original", dp.getDescription() ); + dp.setDescription( "changed" ); + assertEquals( "changed", dp.getDescription() ); + t.commit(); + + assertInsertCount( 0 ); + assertUpdateCount( 0 ); + + s.clear(); + t = s.beginTransaction(); + dp = ( DataPoint ) s.get( DataPoint.class, dp.getId() ); + assertEquals( "original", dp.getDescription() ); + s.delete( dp ); + t.commit(); + s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 1 ); + clearCounts(); + } + + public void testReadOnlyRefreshDetached() { + clearCounts(); + + Session s = openSession(); + Transaction t = s.beginTransaction(); + DataPoint dp = new DataPoint(); + dp.setDescription( "original" ); + 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(); + + assertInsertCount( 1 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); - s.setCacheMode(CacheMode.IGNORE); t = s.beginTransaction(); dp.setDescription( "changed" ); assertEquals( "changed", dp.getDescription() ); @@ -236,6 +320,9 @@ public class ReadOnlyTest extends FunctionalTestCase { assertFalse( s.isReadOnly( dp ) ); t.commit(); + assertInsertCount( 0 ); + assertUpdateCount( 0 ); + s.clear(); t = s.beginTransaction(); dp = ( DataPoint ) s.get( DataPoint.class, dp.getId() ); @@ -243,12 +330,16 @@ public class ReadOnlyTest extends FunctionalTestCase { s.delete( dp ); t.commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 1 ); } public void testReadOnlyDelete() { + clearCounts(); + 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) ); @@ -257,8 +348,11 @@ public class ReadOnlyTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 1 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); - s.setCacheMode(CacheMode.IGNORE); t = s.beginTransaction(); dp = ( DataPoint ) s.get( DataPoint.class, dp.getId() ); s.setReadOnly( dp, true ); @@ -266,6 +360,9 @@ public class ReadOnlyTest extends FunctionalTestCase { t.commit(); s.close(); + assertUpdateCount( 0 ); + assertDeleteCount( 1 ); + s = openSession(); t = s.beginTransaction(); List list = s.createQuery("from DataPoint where description='done!'").list(); @@ -276,9 +373,9 @@ public class ReadOnlyTest extends FunctionalTestCase { } public void testReadOnlyGetModifyAndDelete() { + clearCounts(); 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) ); @@ -287,8 +384,11 @@ public class ReadOnlyTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 1 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); - s.setCacheMode(CacheMode.IGNORE); t = s.beginTransaction(); dp = ( DataPoint ) s.get( DataPoint.class, dp.getId() ); s.setReadOnly( dp, true ); @@ -297,6 +397,10 @@ public class ReadOnlyTest extends FunctionalTestCase { t.commit(); s.close(); + assertUpdateCount( 0 ); + assertDeleteCount( 1 ); + clearCounts(); + s = openSession(); t = s.beginTransaction(); List list = s.createQuery("from DataPoint where description='done!'").list(); @@ -307,9 +411,9 @@ public class ReadOnlyTest extends FunctionalTestCase { } public void testReadOnlyModeWithExistingModifiableEntity() { + clearCounts(); Session s = openSession(); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); DataPoint dp = null; for ( int i=0; i<100; i++ ) { @@ -321,8 +425,11 @@ public class ReadOnlyTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 100 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); - s.setCacheMode(CacheMode.IGNORE); t = s.beginTransaction(); DataPoint dpLast = ( DataPoint ) s.get( DataPoint.class, dp.getId() ); assertFalse( s.isReadOnly( dpLast ) ); @@ -348,18 +455,25 @@ public class ReadOnlyTest extends FunctionalTestCase { } t.commit(); s.clear(); + + assertInsertCount( 0 ); + assertUpdateCount( nExpectedChanges ); + clearCounts(); + t = s.beginTransaction(); List list = s.createQuery("from DataPoint where description='done!'").list(); assertEquals( list.size(), nExpectedChanges ); - s.createQuery("delete from DataPoint").executeUpdate(); + assertEquals( 100, s.createQuery("delete from DataPoint").executeUpdate() ); t.commit(); s.close(); + + assertUpdateCount( 0 ); } public void testModifiableModeWithExistingReadOnlyEntity() { + clearCounts(); Session s = openSession(); - s.setCacheMode(CacheMode.IGNORE); Transaction t = s.beginTransaction(); DataPoint dp = null; for ( int i=0; i<100; i++ ) { @@ -371,14 +485,21 @@ public class ReadOnlyTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 100 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); - s.setCacheMode(CacheMode.IGNORE); t = s.beginTransaction(); DataPoint dpLast = ( DataPoint ) s.get( DataPoint.class, dp.getId() ); assertFalse( s.isReadOnly( dpLast ) ); s.setReadOnly( dpLast, true ); assertTrue( s.isReadOnly( dpLast ) ); + dpLast.setDescription( "oy" ); int i = 0; + + assertUpdateCount( 0 ); + ScrollableResults sr = s.createQuery("from DataPoint dp order by dp.x asc") .setReadOnly(false) .scroll(ScrollMode.FORWARD_ONLY); @@ -400,30 +521,40 @@ public class ReadOnlyTest extends FunctionalTestCase { } t.commit(); s.clear(); + + assertUpdateCount( nExpectedChanges ); + clearCounts(); + t = s.beginTransaction(); List list = s.createQuery("from DataPoint where description='done!'").list(); assertEquals( list.size(), nExpectedChanges ); - s.createQuery("delete from DataPoint").executeUpdate(); + assertEquals( 100, s.createQuery("delete from DataPoint").executeUpdate() ); t.commit(); s.close(); + + assertUpdateCount( 0 ); } public void testReadOnlyOnTextType() { final String origText = "some huge text string"; final String newText = "some even bigger text string"; + clearCounts(); + Session s = openSession(); s.beginTransaction(); - s.setCacheMode( CacheMode.IGNORE ); TextHolder holder = new TextHolder( origText ); s.save( holder ); Long id = holder.getId(); s.getTransaction().commit(); s.close(); + assertInsertCount( 1 ); + assertUpdateCount( 0 ); + clearCounts(); + s = openSession(); s.beginTransaction(); - s.setCacheMode( CacheMode.IGNORE ); holder = ( TextHolder ) s.get( TextHolder.class, id ); s.setReadOnly( holder, true ); holder.setTheText( newText ); @@ -431,6 +562,8 @@ public class ReadOnlyTest extends FunctionalTestCase { s.getTransaction().commit(); s.close(); + assertUpdateCount( 0 ); + s = openSession(); s.beginTransaction(); holder = ( TextHolder ) s.get( TextHolder.class, id ); @@ -438,12 +571,15 @@ public class ReadOnlyTest extends FunctionalTestCase { s.delete( holder ); s.getTransaction().commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 1 ); } public void testMergeWithReadOnlyEntity() { + clearCounts(); 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) ); @@ -452,10 +588,13 @@ public class ReadOnlyTest extends FunctionalTestCase { t.commit(); s.close(); + assertInsertCount( 1 ); + assertUpdateCount( 0 ); + clearCounts(); + 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 ); @@ -464,6 +603,8 @@ public class ReadOnlyTest extends FunctionalTestCase { t.commit(); s.close(); + assertUpdateCount( 0 ); + s = openSession(); t = s.beginTransaction(); dpManaged = ( DataPoint ) s.get( DataPoint.class, new Long( dp.getId() ) ); @@ -472,6 +613,9 @@ public class ReadOnlyTest extends FunctionalTestCase { t.commit(); s.close(); + assertUpdateCount( 0 ); + assertDeleteCount( 1 ); + } } diff --git a/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java b/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java index 599c8db9c8..ffe1842360 100644 --- a/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java +++ b/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyVersionedNodesTest.java @@ -36,7 +36,7 @@ import org.hibernate.cfg.Environment; /** * @author Gail Badner */ -public class ReadOnlyVersionedNodesTest extends FunctionalTestCase { +public class ReadOnlyVersionedNodesTest extends AbstractReadOnlyTest { public ReadOnlyVersionedNodesTest(String str) { super( str ); @@ -46,15 +46,6 @@ public class ReadOnlyVersionedNodesTest extends FunctionalTestCase { 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 ); } @@ -116,15 +107,20 @@ public class ReadOnlyVersionedNodesTest extends FunctionalTestCase { assertUpdateCount( 1 ); assertInsertCount( 0 ); + clearCounts(); s = openSession(); s.beginTransaction(); node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() ); assertEquals( "diff-node-name", node.getName() ); assertEquals( 1, node.getVersion() ); + s.setReadOnly( node, true ); s.delete( node ); s.getTransaction().commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 1 ); } public void testUpdateSetReadOnlyTwice() throws Exception { @@ -155,9 +151,13 @@ public class ReadOnlyVersionedNodesTest extends FunctionalTestCase { node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() ); assertEquals( "node", node.getName() ); assertEquals( 0, node.getVersion() ); + s.setReadOnly( node, true ); s.delete( node ); s.getTransaction().commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 1 ); } public void testUpdateSetModifiable() throws Exception { @@ -181,15 +181,20 @@ public class ReadOnlyVersionedNodesTest extends FunctionalTestCase { assertUpdateCount( 1 ); assertInsertCount( 0 ); + clearCounts(); s = openSession(); s.beginTransaction(); node = ( VersionedNode ) s.get( VersionedNode.class, node.getId() ); assertEquals( "node-name", node.getName() ); assertEquals( 1, node.getVersion() ); + s.setReadOnly( node, true ); s.delete( node ); s.getTransaction().commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 1 ); } public void testUpdateSetReadOnlySetModifiableFailureExpected() throws Exception { @@ -317,6 +322,7 @@ public class ReadOnlyVersionedNodesTest extends FunctionalTestCase { assertUpdateCount( 0 ); assertInsertCount( 1 ); + clearCounts(); s = openSession(); s.beginTransaction(); @@ -328,10 +334,15 @@ public class ReadOnlyVersionedNodesTest extends FunctionalTestCase { assertSame( parent, child.getParent() ); assertSame( child, parent.getChildren().iterator().next() ); assertEquals( 0, child.getVersion() ); + s.setReadOnly( parent, true ); + s.setReadOnly( child, true ); s.delete( parent ); s.delete( child ); s.getTransaction().commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 2 ); } public void testMergeDetachedParentWithNewChildCommitWithReadOnlyParent() throws Exception { @@ -357,6 +368,7 @@ public class ReadOnlyVersionedNodesTest extends FunctionalTestCase { assertUpdateCount( 0 ); assertInsertCount( 1 ); + clearCounts(); s = openSession(); s.beginTransaction(); @@ -368,10 +380,15 @@ public class ReadOnlyVersionedNodesTest extends FunctionalTestCase { assertSame( parent, child.getParent() ); assertSame( child, parent.getChildren().iterator().next() ); assertEquals( 0, child.getVersion() ); + s.setReadOnly( parent, true ); + s.setReadOnly( child, true ); s.delete( parent ); s.delete( child ); s.getTransaction().commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 2 ); } public void testGetParentMakeReadOnlyThenMergeDetachedParentWithNewChildC() throws Exception { @@ -399,6 +416,7 @@ public class ReadOnlyVersionedNodesTest extends FunctionalTestCase { assertUpdateCount( 0 ); assertInsertCount( 1 ); + clearCounts(); s = openSession(); s.beginTransaction(); @@ -414,8 +432,73 @@ public class ReadOnlyVersionedNodesTest extends FunctionalTestCase { s.delete( child ); s.getTransaction().commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 2 ); } + public void testMergeUnchangedDetachedParentChildren() throws Exception { + Session s = openSession(); + s.beginTransaction(); + VersionedNode parent = new VersionedNode( "parent", "parent" ); + VersionedNode child = new VersionedNode( "child", "child"); + parent.addChild( child ); + s.persist( parent ); + s.getTransaction().commit(); + s.close(); + + clearCounts(); + + s = openSession(); + s.beginTransaction(); + parent = ( VersionedNode ) s.merge( parent ); + s.getTransaction().commit(); + s.close(); + + assertUpdateCount( 0 ); + assertInsertCount( 0 ); + clearCounts(); + + s = openSession(); + s.beginTransaction(); + VersionedNode parentGet = ( VersionedNode ) s.get( parent.getClass(), parent.getId() ); + s.merge( parent ); + s.getTransaction().commit(); + s.close(); + + assertUpdateCount( 0 ); + assertInsertCount( 0 ); + clearCounts(); + + s = openSession(); + s.beginTransaction(); + VersionedNode parentLoad = ( VersionedNode ) s.load( parent.getClass(), parent.getId() ); + s.merge( parent ); + s.getTransaction().commit(); + s.close(); + + assertUpdateCount( 0 ); + assertInsertCount( 0 ); + clearCounts(); + + 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(); + + assertUpdateCount( 0 ); + assertDeleteCount( 2 ); + } public void testAddNewParentToReadOnlyChild() throws Exception { Session s = openSession(); @@ -448,9 +531,13 @@ public class ReadOnlyVersionedNodesTest extends FunctionalTestCase { assertEquals( 0, child.getVersion() ); parent = ( VersionedNode ) s.get( VersionedNode.class, parent.getId() ); assertNull( parent ); + s.setReadOnly( child, true ); s.delete( child ); s.getTransaction().commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 1 ); } public void testUpdateChildWithNewParentCommitWithReadOnlyChild() throws Exception { @@ -476,6 +563,7 @@ public class ReadOnlyVersionedNodesTest extends FunctionalTestCase { assertUpdateCount( 0 ); assertInsertCount( 1 ); + clearCounts(); s = openSession(); s.beginTransaction(); @@ -487,10 +575,15 @@ public class ReadOnlyVersionedNodesTest extends FunctionalTestCase { assertNotNull( parent ); assertEquals( 0, parent.getChildren().size() ); assertEquals( 0, parent.getVersion() ); + s.setReadOnly( parent, true ); + s.setReadOnly( child, true ); s.delete( parent ); s.delete( child ); s.getTransaction().commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 2 ); } public void testMergeDetachedChildWithNewParentCommitWithReadOnlyChild() throws Exception { @@ -516,6 +609,7 @@ public class ReadOnlyVersionedNodesTest extends FunctionalTestCase { assertUpdateCount( 1 ); assertInsertCount( 1 ); + clearCounts(); s = openSession(); s.beginTransaction(); @@ -527,10 +621,15 @@ public class ReadOnlyVersionedNodesTest extends FunctionalTestCase { assertNotNull( parent ); assertEquals( 0, parent.getChildren().size() ); assertEquals( 1, parent.getVersion() ); // hmmm, why is was version updated? + s.setReadOnly( parent, true ); + s.setReadOnly( child, true ); s.delete( parent ); s.delete( child ); s.getTransaction().commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 2 ); } public void testGetChildMakeReadOnlyThenMergeDetachedChildWithNewParent() throws Exception { @@ -558,6 +657,7 @@ public class ReadOnlyVersionedNodesTest extends FunctionalTestCase { assertUpdateCount( 1 ); assertInsertCount( 1 ); + clearCounts(); s = openSession(); s.beginTransaction(); @@ -569,10 +669,15 @@ public class ReadOnlyVersionedNodesTest extends FunctionalTestCase { assertNotNull( parent ); assertEquals( 0, parent.getChildren().size() ); assertEquals( 1, parent.getVersion() ); // / hmmm, why is was version updated? + s.setReadOnly( parent, true ); + s.setReadOnly( child, true ); s.delete( parent ); s.delete( child ); s.getTransaction().commit(); s.close(); + + assertUpdateCount( 0 ); + assertDeleteCount( 2 ); } protected void cleanupTest() throws Exception { @@ -590,23 +695,4 @@ public class ReadOnlyVersionedNodesTest extends FunctionalTestCase { 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/criteria/Student.java b/testsuite/src/test/java/org/hibernate/test/readonly/Student.java similarity index 94% rename from testsuite/src/test/java/org/hibernate/test/readonly/criteria/Student.java rename to testsuite/src/test/java/org/hibernate/test/readonly/Student.java index 6ccc16de3a..c0fff9eafb 100755 --- a/testsuite/src/test/java/org/hibernate/test/readonly/criteria/Student.java +++ b/testsuite/src/test/java/org/hibernate/test/readonly/Student.java @@ -1,5 +1,5 @@ //$Id: Student.java 9116 2006-01-23 21:21:01Z steveebersole $ -package org.hibernate.test.readonly.criteria; +package org.hibernate.test.readonly; import java.util.HashSet; import java.util.Set; diff --git a/testsuite/src/test/java/org/hibernate/test/readonly/criteria/StudentDTO.java b/testsuite/src/test/java/org/hibernate/test/readonly/StudentDTO.java similarity index 86% rename from testsuite/src/test/java/org/hibernate/test/readonly/criteria/StudentDTO.java rename to testsuite/src/test/java/org/hibernate/test/readonly/StudentDTO.java index f645fb1d8a..746f7e3166 100644 --- a/testsuite/src/test/java/org/hibernate/test/readonly/criteria/StudentDTO.java +++ b/testsuite/src/test/java/org/hibernate/test/readonly/StudentDTO.java @@ -2,7 +2,7 @@ * Created on 28-Jan-2005 * */ -package org.hibernate.test.readonly.criteria; +package org.hibernate.test.readonly; /** * @author max