[hibernate-commits] Hibernate SVN: r18641 - in core/trunk: core/src/main/java/org/hibernate/engine and 9 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Wed Jan 27 01:36:56 EST 2010


Author: gbadner
Date: 2010-01-27 01:36:55 -0500 (Wed, 27 Jan 2010)
New Revision: 18641

Added:
   core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/Container.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/Info.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/Owner.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlySessionLazyNonLazyTest.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlySessionTest.java
Modified:
   core/trunk/core/src/main/java/org/hibernate/Query.java
   core/trunk/core/src/main/java/org/hibernate/Session.java
   core/trunk/core/src/main/java/org/hibernate/engine/PersistenceContext.java
   core/trunk/core/src/main/java/org/hibernate/engine/QueryParameters.java
   core/trunk/core/src/main/java/org/hibernate/engine/StatefulPersistenceContext.java
   core/trunk/core/src/main/java/org/hibernate/event/def/DefaultLoadEventListener.java
   core/trunk/core/src/main/java/org/hibernate/impl/AbstractQueryImpl.java
   core/trunk/core/src/main/java/org/hibernate/impl/SessionImpl.java
   core/trunk/core/src/main/java/org/hibernate/impl/StatelessSessionImpl.java
   core/trunk/core/src/main/java/org/hibernate/loader/Loader.java
   core/trunk/core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java
   core/trunk/core/src/main/java/org/hibernate/proxy/pojo/cglib/CGLIBLazyInitializer.java
   core/trunk/core/src/main/java/org/hibernate/proxy/pojo/cglib/SerializableProxy.java
   core/trunk/core/src/main/java/org/hibernate/proxy/pojo/javassist/JavassistLazyInitializer.java
   core/trunk/core/src/main/java/org/hibernate/proxy/pojo/javassist/SerializableProxy.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/nonflushedchanges/GetLoadTest.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.hbm.xml
   core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java
Log:
HHH-2501 HHH-4804 : Add default read-only/modifiable setting for session; fix setting for non-lazy associations loaded from Query

Modified: core/trunk/core/src/main/java/org/hibernate/Query.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/Query.java	2010-01-26 21:49:51 UTC (rev 18640)
+++ core/trunk/core/src/main/java/org/hibernate/Query.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -175,11 +175,46 @@
 	public Query setFirstResult(int firstResult);
 	
 	/**
-	 * Entities retrieved by this query will be loaded in 
-	 * a read-only mode where Hibernate will never dirty-check
-	 * them or make changes persistent.
+	 * Should entities and proxies loaded by this Query be put in read-only mode? If the
+	 * read-only/modifiable setting was not initialized, then the default
+	 * read-only/modifiable setting for the persistence context is returned instead.
+	 * @see Query#setReadOnly(boolean)
+	 * @see org.hibernate.engine.PersistenceContext#isDefaultReadOnly()
 	 *
+	 * The read-only/modifiable setting has no impact on entities/proxies returned by the
+	 * query that existed in the session before the query was executed.
+	 *
+	 * @return true, entities and proxies loaded by the query will be put in read-only mode
+	 *         false, entities and proxies loaded by the query will be put in modifiable mode
 	 */
+	public boolean isReadOnly();
+
+	/**
+	 * Set the read-only/modifiable mode for entities and proxies
+	 * loaded by this Query. This setting overrides the default setting
+	 * for the persistence context.
+	 * @see org.hibernate.engine.PersistenceContext#isDefaultReadOnly()
+	 *
+	 * To set the default read-only/modifiable setting used for
+	 * entities and proxies that are loaded into the session:
+	 * @see org.hibernate.engine.PersistenceContext#setDefaultReadOnly(boolean)
+	 * @see org.hibernate.Session#setDefaultReadOnly(boolean)
+	 *
+	 * Read-only entities are not dirty-checked and snapshots of persistent
+	 * state are not maintained. Read-only entities can be modified, but
+	 * changes are not persisted.
+	 *
+	 * When a proxy is initialized, the loaded entity will have the same
+	 * read-only/modifiable setting as the uninitialized
+	 * proxy has, regardless of the session's current setting.
+	 *
+	 * The read-only/modifiable setting has no impact on entities/proxies
+	 * returned by the query that existed in the session before the query was executed.
+	 *
+	 * @return true, entities and proxies loaded by the query will be put in read-only mode
+	 *         false, entities and proxies loaded by the query will be put in modifiable mode
+	 */
+
 	public Query setReadOnly(boolean readOnly);
 
 	/**

Modified: core/trunk/core/src/main/java/org/hibernate/Session.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/Session.java	2010-01-26 21:49:51 UTC (rev 18640)
+++ core/trunk/core/src/main/java/org/hibernate/Session.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -232,6 +232,44 @@
 	public boolean isDirty() throws HibernateException;
 
 	/**
+	 * Will entities and proxies that are loaded into this session be made 
+	 * read-only by default?
+	 *
+	 * To determine the read-only/modifiable setting for a particular entity 
+	 * or proxy:
+	 * @see Session#isReadOnly(Object)
+	 *
+	 * @return true, loaded entities/proxies will be made read-only by default; 
+	 *         false, loaded entities/proxies will be made modifiable by default. 
+	 */
+	public boolean isDefaultReadOnly();
+
+	/**
+	 * Change the default for entities and proxies loaded into this session
+	 * from modifiable to read-only mode, or from modifiable to read-only mode.
+	 *
+	 * Read-only entities are not dirty-checked and snapshots of persistent
+	 * state are not maintained. Read-only entities can be modified, but
+	 * changes are not persisted.
+	 *
+	 * When a proxy is initialized, the loaded entity will have the same
+	 * read-only/modifiable setting as the uninitialized
+	 * proxy has, regardless of the session's current setting.
+	 *
+	 * To change the read-only/modifiable setting for a particular entity
+	 * or proxy that is already in this session:
+	 * @see Session#setReadOnly(Object,boolean)
+	 *
+	 * To override this session's read-only/modifiable setting for entities
+	 * and proxies loaded by a Query:
+	 * @see Query#setReadOnly(boolean)
+	 *
+	 * @param readOnly true, the default for loaded entities/proxies is read-only;
+	 *                 false, the default for loaded entities/proxies is modifiable
+	 */
+	public void setDefaultReadOnly(boolean readOnly);
+
+	/**
 	 * Return the identifier value of the given entity as associated with this
 	 * session.  An exception is thrown if the given entity instance is transient
 	 * or detached in relation to this session.
@@ -842,6 +880,11 @@
 
 	/**
 	 * Is the specified entity or proxy read-only?
+	 *
+	 * To get the default read-only/modifiable setting used for
+	 * entities and proxies that are loaded into the session:
+	 * @see org.hibernate.Session#isDefaultReadOnly()
+	 *
 	 * @param entityOrProxy, an entity or HibernateProxy
 	 * @return true, the entity or proxy is read-only;
 	 *         false, the entity or proxy is modifiable.
@@ -850,16 +893,23 @@
 
 	/**
 	 * Set an unmodified persistent object to read-only mode, or a read-only
-	 * object to modifiable mode. In read-only mode, no snapshot is maintained
-	 * and the instance is never dirty checked.
+	 * object to modifiable mode. In read-only mode, no snapshot is maintained,
+	 * the instance is never dirty checked, and changes are not persisted.
 	 *
 	 * If the entity or proxy already has the specified read-only/modifiable
 	 * setting, then this method does nothing.
 	 * 
+	 * To set the default read-only/modifiable setting used for
+	 * entities and proxies that are loaded into the session:
+	 * @see org.hibernate.Session#setDefaultReadOnly(boolean)
+	 *
+	 * To override this session's read-only/modifiable setting for entities
+	 * and proxies loaded by a Query:
+	 * @see Query#setReadOnly(boolean)
+	 * 
 	 * @param entityOrProxy, an entity or HibernateProxy
 	 * @param readOnly, if true, the entity or proxy is made read-only;
 	 *                  if false, the entity or proxy is made modifiable.
-	 * @see Query#setReadOnly(boolean)
 	 */
 	public void setReadOnly(Object entityOrProxy, boolean readOnly);
 

Modified: core/trunk/core/src/main/java/org/hibernate/engine/PersistenceContext.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/engine/PersistenceContext.java	2010-01-26 21:49:51 UTC (rev 18640)
+++ core/trunk/core/src/main/java/org/hibernate/engine/PersistenceContext.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -477,22 +477,91 @@
 	public boolean isPropertyNull(EntityKey ownerKey, String propertyName);
 
 	/**
+	 * Will entities and proxies that are loaded into this persistence
+	 * context be made read-only by default?
+	 *
+	 * To determine the read-only/modifiable setting for a particular entity
+	 * or proxy:
+	 * @see PersistenceContext#isReadOnly(Object)
+	 * @see org.hibernate.Session#isReadOnly(Object) 
+	 *
+	 * @return true, loaded entities/proxies will be made read-only by default;
+	 *         false, loaded entities/proxies will be made modifiable by default.
+	 *
+	 * @see org.hibernate.Session#isDefaultReadOnly() 
+	 */
+	public boolean isDefaultReadOnly();
+
+	/**
+	 * Change the default for entities and proxies loaded into this persistence
+	 * context from modifiable to read-only mode, or from modifiable to read-only
+	 * mode.
+	 *
+	 * Read-only entities are not dirty-checked and snapshots of persistent
+	 * state are not maintained. Read-only entities can be modified, but
+	 * changes are not persisted.
+	 *
+	 * When a proxy is initialized, the loaded entity will have the same
+	 * read-only/modifiable setting as the uninitialized
+	 * proxy has, regardless of the persistence context's current setting.
+	 *
+	 * To change the read-only/modifiable setting for a particular entity
+	 * or proxy that is already in this session:
++	 * @see PersistenceContext#setReadOnly(Object,boolean)
+	 * @see org.hibernate.Session#setReadOnly(Object, boolean)
+	 *
+	 * To override this session's read-only/modifiable setting for entities
+	 * and proxies loaded by a Query:
+	 * @see org.hibernate.Query#setReadOnly(boolean)
+	 *
+	 * @param readOnly true, the default for loaded entities/proxies is read-only;
+	 *                 false, the default for loaded entities/proxies is modifiable
+	 *
+	 * @see org.hibernate.Session#setDefaultReadOnly(boolean)
+	 */
+	public void setDefaultReadOnly(boolean readOnly);
+
+	/**
 	 * Is the entity or proxy read-only?
 	 *
+	 * To get the default read-only/modifiable setting used for
+	 * entities and proxies that are loaded into the session:
+	 * @see org.hibernate.Session#isDefaultReadOnly()
+	 *
 	 * @param entityOrProxy
 	 * @return true, the object is read-only; false, the object is modifiable.
 	 */
 	public boolean isReadOnly(Object entityOrProxy);
 
 	/**
-	 * Set the entity or proxy to read only and discard it's snapshot.
+	 * Set an unmodified persistent object to read-only mode, or a read-only
+	 * object to modifiable mode.
 	 *
+	 * Read-only entities are not dirty-checked and snapshots of persistent
+	 * state are not maintained. Read-only entities can be modified, but
+	 * changes are not persisted.
+	 *
+	 * When a proxy is initialized, the loaded entity will have the same
+	 * read-only/modifiable setting as the uninitialized
+	 * proxy has, regardless of the session's current setting.
+	 *
 	 * If the entity or proxy already has the specified read-only/modifiable
 	 * setting, then this method does nothing.
 	 *
+	 * To set the default read-only/modifiable setting used for
+	 * entities and proxies that are loaded into this persistence context:
+	 * @see PersistenceContext#setDefaultReadOnly(boolean)
+	 * @see org.hibernate.Session#setDefaultReadOnly(boolean)
+	 *
+	 * To override this persistence context's read-only/modifiable setting
+	 * for entities and proxies loaded by a Query:
+	 * @see org.hibernate.Query#setReadOnly(boolean)
+	 *
 	 * @param entityOrProxy, an entity or HibernateProxy
 	 * @param readOnly, if true, the entity or proxy is made read-only;
 	 *                  if false, the entity or proxy is made modifiable.
+	 *
+	 * @see org.hibernate.Session#setReadOnly(Object, boolean)
 	 */
 	public void setReadOnly(Object entityOrProxy, boolean readOnly);
 

Modified: core/trunk/core/src/main/java/org/hibernate/engine/QueryParameters.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/engine/QueryParameters.java	2010-01-26 21:49:51 UTC (rev 18640)
+++ core/trunk/core/src/main/java/org/hibernate/engine/QueryParameters.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -66,6 +66,7 @@
 	private Object optionalObject;
 	private String optionalEntityName;
 	private Serializable optionalId;
+	private boolean isReadOnlyInitialized;
 	private boolean readOnly;
 	private boolean callable = false;
 	private boolean autodiscovertypes = false;
@@ -124,6 +125,7 @@
 				null,
 				false,
 				false,
+				false,
 				null,
 				null,
 				collectionKeys,
@@ -149,6 +151,7 @@
 				lockOptions,
 				rowSelection,
 				false,
+				false,
 				cacheable,
 				cacheRegion,
 				comment,
@@ -164,6 +167,7 @@
 			final Map namedParameters,
 			final LockOptions lockOptions,
 			final RowSelection rowSelection,
+			final boolean isReadOnlyInitialized,
 			final boolean readOnly,
 			final boolean cacheable,
 			final String cacheRegion,
@@ -181,6 +185,7 @@
 		//this.forceCacheRefresh = forceCacheRefresh;
 		this.comment = comment;
 		this.collectionKeys = collectionKeys;
+		this.isReadOnlyInitialized = isReadOnlyInitialized;
 		this.readOnly = readOnly;
 		this.resultTransformer = transformer;
 	}
@@ -191,6 +196,7 @@
 			final Map namedParameters,
 			final LockOptions lockOptions,
 			final RowSelection rowSelection,
+			final boolean isReadOnlyInitialized,
 			final boolean readOnly,
 			final boolean cacheable,
 			final String cacheRegion,
@@ -207,6 +213,7 @@
 				namedParameters,
 				lockOptions,
 				rowSelection,
+				isReadOnlyInitialized,
 				readOnly,
 				cacheable,
 				cacheRegion,
@@ -351,12 +358,82 @@
 		this.optionalObject = optionalObject;
 	}
 
+	/**
+	 * Has the read-only/modifiable mode been explicitly set?
+	 * @see QueryParameters#setReadOnly(boolean)
+	 * @see QueryParameters#isReadOnly(SessionImplementor)
+	 *
+	 * @return true, the read-only/modifiable mode was explicitly set
+	 *         false, the read-only/modifiable mode was not explicitly set
+	 */
+	public boolean isReadOnlyInitialized() {
+		return isReadOnlyInitialized;
+	}
+
+	/**
+	 * Should entities and proxies loaded by the Query be put in read-only mode? The
+	 * read-only/modifiable setting must be initialized via QueryParameters#setReadOnly(boolean)
+	 * before calling this method.
+	 *
+	 * @see QueryParameters#isReadOnlyInitialized()
+	 * @see QueryParameters#isReadOnly(SessionImplementor)
+	 * @see QueryParameters#setReadOnly(boolean)
+	 *
+	 * The read-only/modifiable setting has no impact on entities/proxies returned by the
+	 * query that existed in the session before the query was executed.
+	 *
+	 * @return true, entities and proxies loaded by the Query will be put in read-only mode
+	 *         false, entities and proxies loaded by the Query will be put in modifiable mode
+	 * @throws IllegalStateException if the read-only/modifiable setting has not been
+	 * initialized (i.e., isReadOnlyInitialized() == false).
+	 */
 	public boolean isReadOnly() {
+		if ( ! isReadOnlyInitialized() ) {
+			throw new IllegalStateException( "cannot call isReadOnly() when isReadOnlyInitialized() returns false" );
+		}
 		return readOnly;
 	}
 
+	/**
+	 * Should entities and proxies loaded by the Query be put in read-only mode? If the
+	 * read-only/modifiable setting was not initialized
+	 * (i.e., QueryParameters#isReadOnlyInitialized() == false), then the default
+	 * read-only/modifiable setting for the persistence context is returned instead.
+	 *
+	 * @see QueryParameters#isReadOnlyInitialized()
+	 * @see QueryParameters#setReadOnly(boolean)
+	 * @see org.hibernate.engine.PersistenceContext#isDefaultReadOnly()
+	 *
+	 * The read-only/modifiable setting has no impact on entities/proxies returned by the
+	 * query that existed in the session before the query was executed.
+	 *
+	 * @return true, entities and proxies loaded by the query will be put in read-only mode
+	 *         false, entities and proxies loaded by the query will be put in modifiable mode
+	 */
+	public boolean isReadOnly(SessionImplementor session) {
+		return ( isReadOnlyInitialized ?
+				isReadOnly() :
+				session.getPersistenceContext().isDefaultReadOnly() 
+		);
+	}
+
+	/**
+	 * Set the read-only/modifiable mode for entities and proxies loaded by the query.
+	 * 	 *
+	 * @see QueryParameters#isReadOnlyInitialized()
+	 * @see QueryParameters#isReadOnly(SessionImplementor)
+	 * @see QueryParameters#setReadOnly(boolean)
+	 * @see org.hibernate.engine.PersistenceContext#isDefaultReadOnly()
+	 *
+	 * The read-only/modifiable setting has no impact on entities/proxies returned by the
+	 * query that existed in the session before the query was executed.
+	 *
+	 * @return true, entities and proxies loaded by the query will be put in read-only mode
+	 *         false, entities and proxies loaded by the query will be put in modifiable mode
+	 */
 	public void setReadOnly(boolean readOnly) {
 		this.readOnly = readOnly;
+		this.isReadOnlyInitialized = true;
 	}
 
 	public void setCallable(boolean callable) {
@@ -467,6 +544,7 @@
 				this.namedParameters,
 				this.lockOptions,
 				selection,
+				this.isReadOnlyInitialized,
 				this.readOnly,
 				this.cacheable,
 				this.cacheRegion,

Modified: core/trunk/core/src/main/java/org/hibernate/engine/StatefulPersistenceContext.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/engine/StatefulPersistenceContext.java	2010-01-26 21:49:51 UTC (rev 18640)
+++ core/trunk/core/src/main/java/org/hibernate/engine/StatefulPersistenceContext.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -127,6 +127,7 @@
 	private int loadCounter = 0;
 	private boolean flushing = false;
 	
+	private boolean defaultReadOnly = false;
 	private boolean hasNonReadOnlyEntities = false;
 
 	private LoadContexts loadContexts;
@@ -231,12 +232,27 @@
 		if ( batchFetchQueue != null ) {
 			batchFetchQueue.clear();
 		}
+		// defaultReadOnly is unaffected by clear()
 		hasNonReadOnlyEntities = false;
 		if ( loadContexts != null ) {
 			loadContexts.cleanup();
 		}
 	}
-	
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isDefaultReadOnly() {
+		return defaultReadOnly;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setDefaultReadOnly(boolean defaultReadOnly) {
+		this.defaultReadOnly = defaultReadOnly;
+	}
+
 	public boolean hasNonReadOnlyEntities() {
 		return hasNonReadOnlyEntities;
 	}
@@ -1396,6 +1412,7 @@
 	public void serialize(ObjectOutputStream oos) throws IOException {
 		log.trace( "serializing persistent-context" );
 
+		oos.writeBoolean( defaultReadOnly );
 		oos.writeBoolean( hasNonReadOnlyEntities );
 
 		oos.writeInt( entitiesByKey.size() );
@@ -1491,6 +1508,7 @@
 		// because serialization is used for different things.
 
 		try {
+			rtn.defaultReadOnly = ois.readBoolean();
 			// todo : we can actually just determine this from the incoming EntityEntry-s
 			rtn.hasNonReadOnlyEntities = ois.readBoolean();
 

Modified: core/trunk/core/src/main/java/org/hibernate/event/def/DefaultLoadEventListener.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/event/def/DefaultLoadEventListener.java	2010-01-26 21:49:51 UTC (rev 18640)
+++ core/trunk/core/src/main/java/org/hibernate/event/def/DefaultLoadEventListener.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -298,6 +298,9 @@
 			Object proxy = persister.createProxy( event.getEntityId(), event.getSession() );
 			persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey(keyToLoad);
 			persistenceContext.addProxy(keyToLoad, proxy);
+			( ( HibernateProxy ) proxy )
+					.getHibernateLazyInitializer()
+					.setReadOnly( event.getSession().isDefaultReadOnly() || ! persister.isMutable() );
 			return proxy;
 		}
 	}
@@ -599,7 +602,10 @@
 		final PersistenceContext persistenceContext = session.getPersistenceContext();
 		persistenceContext.addEntry(
 				result,
-				Status.MANAGED,
+				( session.isDefaultReadOnly() || ! persister.isMutable() ? 
+						Status.READ_ONLY :
+						Status.MANAGED
+				),
 				values,
 				null,
 				id,

Modified: core/trunk/core/src/main/java/org/hibernate/impl/AbstractQueryImpl.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/impl/AbstractQueryImpl.java	2010-01-26 21:49:51 UTC (rev 18640)
+++ core/trunk/core/src/main/java/org/hibernate/impl/AbstractQueryImpl.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -101,7 +101,7 @@
 	private FlushMode sessionFlushMode;
 	private CacheMode sessionCacheMode;
 	private Serializable collectionKey;
-	private boolean readOnly;
+	private Boolean readOnly;
 	private ResultTransformer resultTransformer;
 
 	public AbstractQueryImpl(
@@ -202,12 +202,21 @@
 		return this;
 	}
 
+	/**
+	 * {@inheritDoc}
+	 */
 	public boolean isReadOnly() {
-		return readOnly;
+		return ( readOnly == null ?
+				getSession().getPersistenceContext().isDefaultReadOnly() :
+				readOnly.booleanValue() 
+		);
 	}
 
+	/**
+	 * {@inheritDoc}
+	 */
 	public Query setReadOnly(boolean readOnly) {
-		this.readOnly = readOnly;
+		this.readOnly = Boolean.valueOf( readOnly );
 		return this;
 	}
 
@@ -881,7 +890,8 @@
 				namedParams,
 				getLockOptions(),
 				getSelection(),
-				readOnly,
+				true,
+				isReadOnly(),
 				cacheable,
 				cacheRegion,
 				comment,

Modified: core/trunk/core/src/main/java/org/hibernate/impl/SessionImpl.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/impl/SessionImpl.java	2010-01-26 21:49:51 UTC (rev 18640)
+++ core/trunk/core/src/main/java/org/hibernate/impl/SessionImpl.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -1956,6 +1956,20 @@
 		return true;
 	}
 
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isDefaultReadOnly() {
+		return persistenceContext.isDefaultReadOnly();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setDefaultReadOnly(boolean defaultReadOnly) {
+		persistenceContext.setDefaultReadOnly( defaultReadOnly );
+	}
+
 	public boolean isReadOnly(Object entityOrProxy) {
 		errorIfClosed();
 		checkTransactionSynchStatus();

Modified: core/trunk/core/src/main/java/org/hibernate/impl/StatelessSessionImpl.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/impl/StatelessSessionImpl.java	2010-01-26 21:49:51 UTC (rev 18640)
+++ core/trunk/core/src/main/java/org/hibernate/impl/StatelessSessionImpl.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -483,6 +483,22 @@
 		return false;
 	}
 
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isDefaultReadOnly() {
+		return false;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setDefaultReadOnly(boolean readOnly) throws HibernateException {
+		if ( readOnly == true ) {
+			throw new UnsupportedOperationException();
+		}
+	}
+
 /////////////////////////////////////////////////////////////////////////////////////////////////////
 
 	//TODO: COPY/PASTE FROM SessionImpl, pull up!

Modified: core/trunk/core/src/main/java/org/hibernate/loader/Loader.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/Loader.java	2010-01-26 21:49:51 UTC (rev 18640)
+++ core/trunk/core/src/main/java/org/hibernate/loader/Loader.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -253,15 +253,32 @@
 		throws HibernateException, SQLException {
 
 		final PersistenceContext persistenceContext = session.getPersistenceContext();
+		boolean defaultReadOnlyOrig = persistenceContext.isDefaultReadOnly();
+		if ( queryParameters.isReadOnlyInitialized() ) {
+			// The read-only/modifiable mode for the query was explicitly set.
+			// Temporarily set the default read-only/modifiable setting to the query's setting.
+			persistenceContext.setDefaultReadOnly( queryParameters.isReadOnly() );
+		}
+		else {
+			// The read-only/modifiable setting for the query was not initialized.
+			// Use the default read-only/modifiable from the persistence context instead.
+			queryParameters.setReadOnly( persistenceContext.isDefaultReadOnly() );
+		}
 		persistenceContext.beforeLoad();
 		List result;
 		try {
-			result = doQuery( session, queryParameters, returnProxies );
+			try {
+				result = doQuery( session, queryParameters, returnProxies );
+			}
+			finally {
+				persistenceContext.afterLoad();
+			}
+			persistenceContext.initializeNonLazyCollections();
 		}
 		finally {
-			persistenceContext.afterLoad();
+			// Restore the original default
+			persistenceContext.setDefaultReadOnly( defaultReadOnlyOrig );
 		}
-		persistenceContext.initializeNonLazyCollections();
 		return result;
 	}
 
@@ -312,7 +329,7 @@
 				hydratedObjects, 
 				resultSet, 
 				session, 
-				queryParameters.isReadOnly() 
+				queryParameters.isReadOnly( session )
 			);
 		session.getPersistenceContext().initializeNonLazyCollections();
 		return result;
@@ -363,7 +380,7 @@
 				hydratedObjects, 
 				resultSet, 
 				session, 
-				queryParameters.isReadOnly() 
+				queryParameters.isReadOnly( session )
 			);
 		session.getPersistenceContext().initializeNonLazyCollections();
 		return result;
@@ -749,7 +766,7 @@
 			session.getBatcher().closeQueryStatement( st, rs );
 		}
 
-		initializeEntitiesAndCollections( hydratedObjects, rs, session, queryParameters.isReadOnly() );
+		initializeEntitiesAndCollections( hydratedObjects, rs, session, queryParameters.isReadOnly( session ) );
 
 		if ( createSubselects ) createSubselects( subselectResultKeys, queryParameters, session );
 
@@ -1884,7 +1901,7 @@
 		try {
 			result = doQueryAndInitializeNonLazyCollections( 
 					session,
-					new QueryParameters( 
+					new QueryParameters(
 							new Type[] { identifierType },
 							new Object[] { id },
 							optionalObject,

Modified: core/trunk/core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java	2010-01-26 21:49:51 UTC (rev 18640)
+++ core/trunk/core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -65,11 +65,11 @@
 	protected AbstractLazyInitializer(String entityName, Serializable id, SessionImplementor session) {
 		this.entityName = entityName;
 		this.id = id;
+		this.readOnly = false;
 		// initialize other fields depending on session state
 		if ( session == null ) {
 			// would be better to call unsetSession(), but it is not final...
 			session = null;
-			readOnly = false;
 		}
 		else {
 			setSession( session );
@@ -116,11 +116,11 @@
 	 */
 	public final void setSession(SessionImplementor s) throws HibernateException {
 		if ( s != session ) {
+			readOnly = false;
 			// check for s == null first, since it is least expensive
 			if ( s == null ){
 				// would be better to call unsetSession(), but it is not final...
 				session = null;
-				readOnly = false;
 			}
 			else if ( isConnectedToSession() ) {
 				//TODO: perhaps this should be some other RuntimeException...
@@ -128,8 +128,6 @@
 			}
 			else {
 				session = s;
-				// NOTE: the proxy may not be connected to the session yet, so set readOnly directly
-				readOnly = ! session.getFactory().getEntityPersister( entityName ).isMutable();
 			}
 		}
 	}

Modified: core/trunk/core/src/main/java/org/hibernate/proxy/pojo/cglib/CGLIBLazyInitializer.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/proxy/pojo/cglib/CGLIBLazyInitializer.java	2010-01-26 21:49:51 UTC (rev 18640)
+++ core/trunk/core/src/main/java/org/hibernate/proxy/pojo/cglib/CGLIBLazyInitializer.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -212,6 +212,7 @@
 				persistentClass,
 				interfaces,
 				getIdentifier(),
+				( getSession() != null && getSession().isOpen() ? isReadOnly() : false ),
 				getIdentifierMethod,
 				setIdentifierMethod,
 				componentIdType 

Modified: core/trunk/core/src/main/java/org/hibernate/proxy/pojo/cglib/SerializableProxy.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/proxy/pojo/cglib/SerializableProxy.java	2010-01-26 21:49:51 UTC (rev 18640)
+++ core/trunk/core/src/main/java/org/hibernate/proxy/pojo/cglib/SerializableProxy.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -28,6 +28,7 @@
 import java.lang.reflect.Method;
 
 import org.hibernate.HibernateException;
+import org.hibernate.proxy.HibernateProxy;
 import org.hibernate.type.AbstractComponentType;
 
 /**
@@ -39,6 +40,7 @@
 	private Class persistentClass;
 	private Class[] interfaces;
 	private Serializable id;
+	private boolean readOnly;
 	private Class getIdentifierMethodClass;
 	private Class setIdentifierMethodClass;
 	private String getIdentifierMethodName;
@@ -53,6 +55,7 @@
 		final Class persistentClass,
 		final Class[] interfaces,
 		final Serializable id,
+		final boolean readOnly,
 		final Method getIdentifierMethod,
 		final Method setIdentifierMethod,
 		AbstractComponentType componentIdType
@@ -61,6 +64,7 @@
 		this.persistentClass = persistentClass;
 		this.interfaces = interfaces;
 		this.id = id;
+		this.readOnly = readOnly;
 		if (getIdentifierMethod!=null) {
 			getIdentifierMethodClass = getIdentifierMethod.getDeclaringClass();
 			getIdentifierMethodName = getIdentifierMethod.getName();

Modified: core/trunk/core/src/main/java/org/hibernate/proxy/pojo/javassist/JavassistLazyInitializer.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/proxy/pojo/javassist/JavassistLazyInitializer.java	2010-01-26 21:49:51 UTC (rev 18640)
+++ core/trunk/core/src/main/java/org/hibernate/proxy/pojo/javassist/JavassistLazyInitializer.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -229,6 +229,7 @@
 		        persistentClass,
 		        interfaces,
 		        getIdentifier(),
+		        ( getSession() != null && getSession().isOpen() ? isReadOnly() : false ),
 		        getIdentifierMethod,
 		        setIdentifierMethod,
 		        componentIdType

Modified: core/trunk/core/src/main/java/org/hibernate/proxy/pojo/javassist/SerializableProxy.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/proxy/pojo/javassist/SerializableProxy.java	2010-01-26 21:49:51 UTC (rev 18640)
+++ core/trunk/core/src/main/java/org/hibernate/proxy/pojo/javassist/SerializableProxy.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -28,6 +28,7 @@
 import java.lang.reflect.Method;
 
 import org.hibernate.HibernateException;
+import org.hibernate.proxy.HibernateProxy;
 import org.hibernate.type.AbstractComponentType;
 
 /**
@@ -39,6 +40,7 @@
 	private Class persistentClass;
 	private Class[] interfaces;
 	private Serializable id;
+	private boolean readOnly;
 	private Class getIdentifierMethodClass;
 	private Class setIdentifierMethodClass;
 	private String getIdentifierMethodName;
@@ -53,6 +55,7 @@
 	    final Class persistentClass,
 	    final Class[] interfaces,
 	    final Serializable id,
+	    final boolean readOnly,
 	    final Method getIdentifierMethod,
 	    final Method setIdentifierMethod,
 	    AbstractComponentType componentIdType
@@ -61,6 +64,7 @@
 		this.persistentClass = persistentClass;
 		this.interfaces = interfaces;
 		this.id = id;
+		this.readOnly = readOnly;
 		if (getIdentifierMethod!=null) {
 			getIdentifierMethodClass = getIdentifierMethod.getDeclaringClass();
 			getIdentifierMethodName = getIdentifierMethod.getName();

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java	2010-01-26 21:49:51 UTC (rev 18640)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/immutable/ImmutableTest.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -29,6 +29,7 @@
 
 import junit.framework.Test;
 
+import org.hibernate.Hibernate;
 import org.hibernate.HibernateException;
 import org.hibernate.Session;
 import org.hibernate.Transaction;
@@ -53,6 +54,120 @@
 		return new FunctionalTestClassTestSuite( ImmutableTest.class );
 	}
 
+	public void testPersistImmutable() {
+		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);
+		// 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 ) );
+		t.commit();
+		s.close();
+
+		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();
+	}
+
+	public void testSaveImmutable() {
+		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.save(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 ) );
+		t.commit();
+		s.close();
+
+		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();
+	}
+
+	public void testSaveOrUpdateImmutable() {
+		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.saveOrUpdate(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 ) );
+		t.commit();
+		s.close();
+
+		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();
+	}
+
 	public void testImmutable() {
 		Contract c = new Contract("gavin", "phone");
 		ContractVariation cv1 = new ContractVariation(1, c);
@@ -62,22 +177,36 @@
 		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 ) );
 		t.commit();
 		s.close();
 		
 		s = openSession();
 		t = s.beginTransaction();
 		c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+		// c was loaded into s, so it should be read-only
+		assertTrue( s.isReadOnly( c ) );
 		c.setCustomerName("foo bar");
 		c.getVariations().add( new ContractVariation(3, c) );
 		cv1 = (ContractVariation) c.getVariations().iterator().next();
 		cv1.setText("blah blah");
+		// cv1 and cv2 were loaded into s by hibernate, so they should be read-only
+		assertTrue( s.isReadOnly( cv1 ) );
+		assertFalse( s.contains( cv2 ) );
 		t.commit();
+		assertTrue( s.isReadOnly( c ) );
+		assertTrue( s.isReadOnly( cv1 ) );
+		assertFalse( s.contains( cv2 ) );
 		s.close();
 		
 		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();
@@ -85,6 +214,9 @@
 		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) );
@@ -92,6 +224,209 @@
 		s.close();
 	}
 
+	public void testPersistAndUpdateImmutable() {
+		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);
+		// 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( "Sherman" );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		c = (Contract) s.createCriteria(Contract.class).uniqueResult();
+		// c was loaded into s, so it should be read-only
+		assertTrue( s.isReadOnly( c ) );
+		c.setCustomerName("foo bar");
+		c.getVariations().add( new ContractVariation(3, c) );
+		cv1 = (ContractVariation) c.getVariations().iterator().next();
+		cv1.setText("blah blah");
+		// cv1 and cv2 were loaded into s by hibernate, so they should be read-only
+		assertTrue( s.isReadOnly( cv1 ) );
+		assertFalse( s.contains( cv2 ) );
+		t.commit();
+		assertTrue( s.isReadOnly( c ) );
+		assertTrue( s.isReadOnly( cv1 ) );
+		assertFalse( s.contains( cv2 ) );
+		s.close();
+
+		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();
+	}
+
+	public void testUpdateAndDeleteManagedImmutable() {
+		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 = (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 ) );
+		c.setCustomerName( "Sherman" );
+		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();
+	}
+
+	public void testGetAndDeleteManagedImmutable() {
+		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 = (Contract) s.get( Contract.class, c.getId() );
+		// 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 ) );
+		c.setCustomerName( "Sherman" );
+		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();
+	}
+
+	public void testDeleteDetachedImmutable() {
+		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();
+		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();
+	}
+
+	public void testDeleteDetachedModifiedImmutable() {
+		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( "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();
+	}
+
+
 	public void testImmutableParentEntityWithUpdate() {
 		Contract c = new Contract("gavin", "phone");
 		ContractVariation cv1 = new ContractVariation(1, c);
@@ -108,7 +443,15 @@
 		t = s.beginTransaction();
 		c.setCustomerName("foo bar");
 		s.update( c );
+		// c was not loaded into s by hibernate, so it should be modifiable
+		assertFalse( s.isReadOnly( c ) );
+		assertFalse( s.contains( cv1 ) );
+		assertFalse( s.contains( cv2 ) );
 		t.commit();
+		// c, cv1, and cv2 were not loaded into s by hibernate, so they are modifiable
+		assertFalse( s.isReadOnly( c ) );
+		assertFalse( s.isReadOnly( cv1 ) );
+		assertFalse( s.isReadOnly( cv2 ) );		
 		s.close();
 
 		s = openSession();
@@ -145,7 +488,14 @@
 		cv1 = (ContractVariation) c.getVariations().iterator().next();
 		cv1.setText("blah blah");
 		s.update( c );
+		// c was not loaded into s by hibernate, so it should be modifiable
+		assertFalse( s.isReadOnly( c ) );
+		assertFalse( s.contains( cv1 ) );
+		assertFalse( s.contains( cv2 ) );
 		t.commit();
+		assertFalse( s.isReadOnly( c ) );
+		assertFalse( s.isReadOnly( cv1 ) );
+		assertFalse( s.isReadOnly( cv2 ) );
 		s.close();
 
 		s = openSession();
@@ -224,7 +574,16 @@
 		s = openSession();
 		t = s.beginTransaction();
 		c.setCustomerName("foo bar");
-		s.merge( c );
+		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( c ) );
+		assertTrue( s.isReadOnly( c ) );
 		t.commit();
 		s.close();
 
@@ -233,7 +592,7 @@
 		c = (Contract) s.createCriteria(Contract.class).uniqueResult();
 		assertEquals( c.getCustomerName(), "gavin" );
 		assertEquals( c.getVariations().size(), 2 );
-		Iterator it = c.getVariations().iterator();
+		it = c.getVariations().iterator();
 		cv1 = (ContractVariation) it.next();
 		assertEquals( cv1.getText(), "expensive" );
 		cv2 = (ContractVariation) it.next();
@@ -261,7 +620,16 @@
 		t = s.beginTransaction();
 		cv1 = (ContractVariation) c.getVariations().iterator().next();
 		cv1.setText("blah blah");
-		s.merge( c );
+		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( c ) );
+		assertTrue( s.isReadOnly( c ) );
 		t.commit();
 		s.close();
 
@@ -270,7 +638,7 @@
 		c = (Contract) s.createCriteria(Contract.class).uniqueResult();
 		assertEquals( c.getCustomerName(), "gavin" );
 		assertEquals( c.getVariations().size(), 2 );
-		Iterator it = c.getVariations().iterator();
+		it = c.getVariations().iterator();
 		cv1 = (ContractVariation) it.next();
 		assertEquals( cv1.getText(), "expensive" );
 		cv2 = (ContractVariation) it.next();

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/nonflushedchanges/GetLoadTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/nonflushedchanges/GetLoadTest.java	2010-01-26 21:49:51 UTC (rev 18640)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/nonflushedchanges/GetLoadTest.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -1,6 +1,9 @@
 //$Id: GetLoadTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at jboss.com $
 package org.hibernate.test.nonflushedchanges;
 
+import java.util.Iterator;
+import java.util.List;
+
 import junit.framework.Test;
 
 import org.hibernate.Hibernate;
@@ -8,6 +11,7 @@
 import org.hibernate.cfg.Configuration;
 import org.hibernate.cfg.Environment;
 import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+import org.hibernate.proxy.HibernateProxy;
 import org.hibernate.test.tm.SimpleJtaTransactionManagerImpl;
 
 
@@ -92,8 +96,133 @@
 		SimpleJtaTransactionManagerImpl.getInstance().commit();
 
 		assertFetchCount( 0 );
+
+		SimpleJtaTransactionManagerImpl.getInstance().begin();
+		s = openSession();
+		s.createQuery( "delete from Employer" ).executeUpdate();
+		List list = s.createQuery( "from Node" ).list();
+		for ( Iterator it=list.iterator(); it.hasNext(); ) {
+			s.delete( it.next() );
+		}
+		SimpleJtaTransactionManagerImpl.getInstance().commit();
 	}
 
+	public void testGetReadOnly() throws Exception {
+		clearCounts();
+
+		SimpleJtaTransactionManagerImpl.getInstance().begin();
+		Session s = openSession();
+		Employer emp = new Employer();
+		s.persist( emp );
+		Node node = new Node( "foo" );
+		Node parent = new Node( "bar" );
+		parent.addChild( node );
+		s.persist( parent );
+		SimpleJtaTransactionManagerImpl.getInstance().commit();
+
+		SimpleJtaTransactionManagerImpl.getInstance().begin();
+		s = openSession();
+		assertFalse( s.isDefaultReadOnly() );
+		s.setDefaultReadOnly( true );
+		emp = ( Employer ) s.get( Employer.class, emp.getId() );
+		assertTrue( s.isDefaultReadOnly() );
+		s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
+		assertTrue( s.isDefaultReadOnly() );
+		emp = ( Employer ) getOldToNewEntityRefMap().get( emp );
+		assertTrue( Hibernate.isInitialized( emp ) );
+		assertFalse( Hibernate.isInitialized( emp.getEmployees() ) );
+		node = ( Node ) s.get( Node.class, node.getName() );
+		assertTrue( s.isReadOnly( emp ) );
+		assertTrue( s.isReadOnly( node ) );
+		s.setDefaultReadOnly( false );
+		s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
+		assertFalse( s.isDefaultReadOnly() );
+		node = ( Node ) getOldToNewEntityRefMap().get( node );
+		emp = ( Employer ) getOldToNewEntityRefMap().get( emp );
+		assertTrue( Hibernate.isInitialized( node ) );
+		assertTrue( s.isReadOnly( node ) );
+		assertFalse( Hibernate.isInitialized( node.getParent() ) );
+		assertTrue( s.isReadOnly( emp ) );
+		assertFalse( Hibernate.isInitialized( node.getChildren() ) );
+		Hibernate.initialize( node.getChildren() );
+		for ( Iterator it=node.getChildren().iterator(); it.hasNext(); ) {
+			assertFalse( s.isReadOnly( it.next() ) );
+		}
+		assertFalse( Hibernate.isInitialized( node.getParent() ) );
+		assertNull( s.get( Node.class, "xyz" ) );
+		SimpleJtaTransactionManagerImpl.getInstance().commit();
+
+		SimpleJtaTransactionManagerImpl.getInstance().begin();
+		s = openSession();
+		assertFalse( s.isDefaultReadOnly() );
+		emp = ( Employer ) s.get( "org.hibernate.test.nonflushedchanges.Employer", emp.getId() );
+		s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
+		assertFalse( s.isDefaultReadOnly() );
+		emp = ( Employer ) getOldToNewEntityRefMap().get( emp );
+		assertTrue( Hibernate.isInitialized( emp ) );
+		assertFalse( s.isReadOnly( emp ) );
+		s.setReadOnly( emp, true );
+		node = ( Node ) s.get( "org.hibernate.test.nonflushedchanges.Node", node.getName() );
+		assertFalse( s.isReadOnly( node ) );
+		s.setReadOnly( node, true );
+		s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
+		emp = ( Employer ) getOldToNewEntityRefMap().get( emp );
+		assertTrue( s.isReadOnly( emp ) );
+		node = ( Node ) getOldToNewEntityRefMap().get( node );
+		assertTrue( Hibernate.isInitialized( node ) );
+		assertTrue( s.isReadOnly( node ) );
+		SimpleJtaTransactionManagerImpl.getInstance().commit();
+
+		assertFetchCount( 0 );
+
+		SimpleJtaTransactionManagerImpl.getInstance().begin();
+		s = openSession();
+		s.createQuery( "delete from Employer" ).executeUpdate();
+		List list = s.createQuery( "from Node" ).list();
+		for ( Iterator it=list.iterator(); it.hasNext(); ) {
+			s.delete( it.next() );
+		}
+		SimpleJtaTransactionManagerImpl.getInstance().commit();
+	}
+
+	public void testLoadReadOnlyFailureExpected() throws Exception {
+		clearCounts();
+
+		SimpleJtaTransactionManagerImpl.getInstance().begin();
+		Session s = openSession();
+		Employer emp = new Employer();
+		s.persist( emp );
+		Node node = new Node( "foo" );
+		Node parent = new Node( "bar" );
+		parent.addChild( node );
+		s.persist( parent );
+		SimpleJtaTransactionManagerImpl.getInstance().commit();
+
+		SimpleJtaTransactionManagerImpl.getInstance().begin();
+		s = openSession();
+		assertFalse( s.isDefaultReadOnly() );
+		s.setDefaultReadOnly( true );
+		emp = ( Employer ) s.load( Employer.class, emp.getId() );
+		assertFalse( Hibernate.isInitialized( emp ) );
+		assertTrue( s.isReadOnly( emp ) );
+		assertTrue( s.isDefaultReadOnly() );
+		s = applyNonFlushedChangesToNewSessionCloseOldSession( s );
+		assertTrue( s.isDefaultReadOnly() );
+		emp = ( Employer ) getOldToNewEntityRefMap().get( emp );
+		assertFalse( Hibernate.isInitialized( emp ) );
+		assertTrue( s.isReadOnly( emp ) );
+		SimpleJtaTransactionManagerImpl.getInstance().commit();
+
+		SimpleJtaTransactionManagerImpl.getInstance().begin();
+		s = openSession();
+		s.createQuery( "delete from Employer" ).executeUpdate();
+		List list = s.createQuery( "from Node" ).list();
+		for ( Iterator it=list.iterator(); it.hasNext(); ) {
+			s.delete( it.next() );
+		}
+		SimpleJtaTransactionManagerImpl.getInstance().commit();
+	}
+
 	public void testGetAfterDelete() throws Exception {
 		clearCounts();
 

Added: core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/Container.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/Container.java	                        (rev 0)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/Container.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -0,0 +1,117 @@
+package org.hibernate.test.readonly;
+
+import java.util.Set;
+import java.util.HashSet;
+import java.io.Serializable;
+
+/**
+ * @author Steve Ebersole, Gail Badner (adapted this from "proxy" tests version)
+ */
+public class Container implements Serializable {
+	private Long id;
+	private String name;
+	private Owner noProxyOwner;
+	private Owner proxyOwner;
+	private Owner nonLazyOwner;
+	private Info noProxyInfo;
+	private Info proxyInfo;
+	private Info nonLazyInfo;
+	private Set lazyDataPoints = new HashSet();
+	private Set nonLazyJoinDataPoints = new HashSet();
+	private Set nonLazySelectDataPoints = new HashSet();
+
+	public Container() {
+	}
+
+	public Container(String name) {
+		this.name = name;
+	}
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public Owner getNoProxyOwner() {
+		return noProxyOwner;
+	}
+
+	public void setNoProxyOwner(Owner noProxyOwner) {
+		this.noProxyOwner = noProxyOwner;
+	}
+
+	public Owner getProxyOwner() {
+		return proxyOwner;
+	}
+
+	public void setProxyOwner(Owner proxyOwner) {
+		this.proxyOwner = proxyOwner;
+	}
+
+	public Owner getNonLazyOwner() {
+		return nonLazyOwner;
+	}
+
+	public void setNonLazyOwner(Owner nonLazyOwner) {
+		this.nonLazyOwner = nonLazyOwner;
+	}
+
+	public Info getNoProxyInfo() {
+		return noProxyInfo;
+	}
+
+	public void setNoProxyInfo(Info noProxyInfo) {
+		this.noProxyInfo = noProxyInfo;
+	}
+
+	public Info getProxyInfo() {
+		return proxyInfo;
+	}
+
+	public void setProxyInfo(Info proxyInfo) {
+		this.proxyInfo = proxyInfo;
+	}
+
+	public Info getNonLazyInfo() {
+		return nonLazyInfo;
+	}
+
+	public void setNonLazyInfo(Info nonLazyInfo) {
+		this.nonLazyInfo = nonLazyInfo;
+	}
+
+	public Set getLazyDataPoints() {
+		return lazyDataPoints;
+	}
+
+	public void setLazyDataPoints(Set lazyDataPoints) {
+		this.lazyDataPoints = lazyDataPoints;
+	}
+
+	public Set getNonLazyJoinDataPoints() {
+		return nonLazyJoinDataPoints;
+	}
+
+	public void setNonLazyJoinDataPoints(Set nonLazyJoinDataPoints) {
+		this.nonLazyJoinDataPoints = nonLazyJoinDataPoints;
+	}
+
+	public Set getNonLazySelectDataPoints() {
+		return nonLazySelectDataPoints;
+	}
+
+	public void setNonLazySelectDataPoints(Set nonLazySelectDataPoints) {
+		this.nonLazySelectDataPoints = nonLazySelectDataPoints;
+	}
+}
\ No newline at end of file

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.hbm.xml
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.hbm.xml	2010-01-26 21:49:51 UTC (rev 18640)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.hbm.xml	2010-01-27 06:36:55 UTC (rev 18641)
@@ -21,4 +21,43 @@
 		<property name="description"/>
 	</class>
 
+    <class name="Owner">
+        <id name="id">
+            <generator class="increment"/>
+        </id>
+        <property name="name" unique="true"/>
+    </class>
+
+    <class name="Info">
+        <id name="id">
+            <generator class="increment"/>
+        </id>
+        <property name="details"/>
+    </class>
+
+    <class name="Container">
+        <id name="id">
+            <generator class="increment"/>
+        </id>
+        <property name="name"/>
+        <many-to-one name="noProxyOwner" class="Owner" column="no_proxy_owner_name" property-ref="name" lazy="no-proxy" cascade="all"/>
+        <many-to-one name="proxyOwner" class="Owner" column="proxy_owner_name" property-ref="name" lazy="proxy" cascade="all"/>
+        <many-to-one name="nonLazyOwner" class="Owner" column="non_lazy_owner_name" property-ref="name" lazy="false" cascade="all"/>
+        <many-to-one name="noProxyInfo" class="Info" column="no_proxy_info_id" lazy="no-proxy" cascade="all"/>
+        <many-to-one name="proxyInfo" class="Info" column="proxy_info_id" lazy="proxy" cascade="all"/>
+        <many-to-one name="nonLazyInfo" class="Info" column="non_lazy_info_id" lazy="false" cascade="all"/>
+        <set name="lazyDataPoints" lazy="true" inverse="false" cascade="all">
+            <key column="c_lazy_id"/>
+            <one-to-many class="DataPoint"/>
+        </set>
+        <set name="nonLazySelectDataPoints" lazy="false" inverse="false" cascade="all" fetch="select">
+            <key column="c_non_lazy_select_id"/>
+            <one-to-many class="DataPoint"/>
+        </set>
+        <set name="nonLazyJoinDataPoints" lazy="false" inverse="false" cascade="all" fetch="join">
+            <key column="c_non_lazy_join_id"/>
+            <one-to-many class="DataPoint"/>
+        </set>
+    </class>
+    
 </hibernate-mapping>

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.java	2010-01-26 21:49:51 UTC (rev 18640)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/DataPoint.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -2,15 +2,25 @@
 package org.hibernate.test.readonly;
 
 import java.math.BigDecimal;
+import java.io.Serializable;
 
 /**
  * @author Gavin King
  */
-public class DataPoint {
+public class DataPoint implements Serializable {
 	private long id;
 	private BigDecimal x;
 	private BigDecimal y;
 	private String description;
+
+	public DataPoint() {}
+
+	public DataPoint(BigDecimal x, BigDecimal y, String description) {
+		this.x = x;
+		this.y = y;
+		this.description = description;
+	}
+
 	/**
 	 * @return Returns the description.
 	 */
@@ -59,4 +69,7 @@
 	public void setY(BigDecimal y) {
 		this.y = y;
 	}
+	void exception() throws Exception {
+		throw new Exception("foo");
+	}
 }

Added: core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/Info.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/Info.java	                        (rev 0)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/Info.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -0,0 +1,34 @@
+package org.hibernate.test.readonly;
+
+/**
+ * todo: describe Info
+ *
+ * @author Steve Ebersole, Gail Badner (adapted this from "proxy" tests version)
+ */
+public class Info {
+	private Long id;
+	private String details;
+
+	public Info() {
+	}
+
+	public Info(String details) {
+		this.details = details;
+	}
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getDetails() {
+		return details;
+	}
+
+	public void setDetails(String details) {
+		this.details = details;
+	}
+}
\ No newline at end of file

Added: core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/Owner.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/Owner.java	                        (rev 0)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/Owner.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -0,0 +1,34 @@
+package org.hibernate.test.readonly;
+
+import java.io.Serializable;
+
+/**
+ * @author Steve Ebersole, Gail Badner (adapted this from "proxy" tests version)
+ */
+public class Owner implements Serializable {
+	private Long id;
+	private String name;
+
+	public Owner() {
+	}
+
+	public Owner(String name) {
+		this.name = name;
+	}
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+}
\ No newline at end of file

Added: core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlySessionLazyNonLazyTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlySessionLazyNonLazyTest.java	                        (rev 0)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlySessionLazyNonLazyTest.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -0,0 +1,1446 @@
+//$Id: ReadOnlyTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at 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;
+import java.util.List;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Arrays;
+import java.util.Iterator;
+
+import junit.framework.Test;
+
+import org.hibernate.CacheMode;
+import org.hibernate.Hibernate;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Model:
+ *
+ * Container
+ * |-- N : 1 -- noProxyOwner (property-ref="name" lazy="no-proxy" cascade="all")
+ * |-- N : 1 -- proxyOwner (property-ref="name" lazy="proxy" cascade="all")
+ * |-- N : 1 -- nonLazyOwner (property-ref="name" lazy="false" cascade="all")
+ * |-- N : 1 -- noProxyInfo" (lazy="no-proxy" cascade="all")
+ * |-- N : 1 -- proxyInfo (lazy="proxy" cascade="all"
+ * |-- N : 1 -- nonLazyInfo" (lazy="false" cascade="all")
+ * |
+ * |-- 1 : N -- lazyDataPoints" (lazy="true" inverse="false" cascade="all")
+ * |-- 1 : N -- nonLazySelectDataPoints" (lazy="false" inverse="false" cascade="all" fetch="select")
+ * |-- 1 : N -- nonLazyJoinDataPoints" (lazy="false" inverse="false" cascade="all" fetch="join")
+ *
+ * Note: the following many-to-one properties use a property-ref so they are
+ * initialized, regardless of how the lazy attribute is mapped:
+ * noProxyOwner, proxyOwner, nonLazyOwner
+ *
+ * @author Gail Badner
+ */
+public class ReadOnlySessionLazyNonLazyTest extends FunctionalTestCase {
+
+	public ReadOnlySessionLazyNonLazyTest(String str) {
+		super(str);
+	}
+
+	public String[] getMappings() {
+		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 );
+	}
+
+	public void testExistingModifiableAfterSetSessionReadOnly() {
+
+		Container cOrig = createContainer();
+		Set expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						cOrig, cOrig.getNoProxyInfo(), cOrig.getProxyInfo(), cOrig.getNonLazyInfo(),
+						cOrig.getNoProxyOwner(), cOrig.getProxyOwner(), cOrig.getNonLazyOwner(),
+						cOrig.getLazyDataPoints().iterator().next(),
+						cOrig.getNonLazyJoinDataPoints().iterator().next(),
+						cOrig.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		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 );
+		s.setDefaultReadOnly( true );
+		assertTrue( s.isDefaultReadOnly() );
+		checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+
+		t = s.beginTransaction();
+		assertTrue( s.isDefaultReadOnly() );
+		checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		Container c = ( Container ) s.load( Container.class, cOrig.getId() );
+		assertSame( cOrig, c );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		c = ( Container ) s.get( Container.class, cOrig.getId() );
+		assertSame( cOrig, c );
+		checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		//s.refresh( cOrig );
+		assertSame( cOrig, c );
+		checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		s.evict( cOrig );
+		c = ( Container ) s.get( Container.class, cOrig.getId()  );
+		assertNotSame( cOrig, c );
+		expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						c, c.getNonLazyInfo(),
+						c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+						c.getNonLazyJoinDataPoints().iterator().next(),
+						c.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		expectedReadOnlyObjects =
+				new HashSet(
+						Arrays.asList( new Object[ ] {
+								c, c.getNoProxyInfo(), c.getProxyInfo(), c.getNonLazyInfo(),
+								c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+								//c.getLazyDataPoints(),
+								c.getNonLazyJoinDataPoints().iterator().next(),
+								c.getNonLazySelectDataPoints().iterator().next()
+						}
+				)
+		);
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getNoProxyInfo() ) );
+		Hibernate.initialize( c.getNoProxyInfo() );
+		expectedInitializedObjects.add( c.getNoProxyInfo() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getProxyInfo() ) );
+		Hibernate.initialize( c.getProxyInfo() );
+		expectedInitializedObjects.add( c.getProxyInfo() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getLazyDataPoints() ) );
+		Hibernate.initialize( c.getLazyDataPoints() );
+		expectedInitializedObjects.add( c.getLazyDataPoints().iterator().next() );
+		expectedReadOnlyObjects.add(c.getLazyDataPoints().iterator().next() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+		s = openSession();
+		t = s.beginTransaction();
+		s.createQuery("delete from DataPoint").executeUpdate();
+		s.createQuery("delete from Container").executeUpdate();
+		s.createQuery("delete from Info").executeUpdate();
+		s.createQuery("delete from Owner").executeUpdate();
+		t.commit();
+		s.close();
+
+	}
+
+	public void testExistingReadOnlyAfterSetSessionModifiable() {
+
+		Container cOrig = createContainer();
+		Set expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						cOrig, cOrig.getNoProxyInfo(), cOrig.getProxyInfo(), cOrig.getNonLazyInfo(),
+						cOrig.getNoProxyOwner(), cOrig.getProxyOwner(), cOrig.getNonLazyOwner(),
+						cOrig.getLazyDataPoints().iterator().next(),
+						cOrig.getNonLazyJoinDataPoints().iterator().next(),
+						cOrig.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		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 );
+		s.setDefaultReadOnly( true );
+		assertTrue( s.isDefaultReadOnly() );
+		checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		s.setDefaultReadOnly( true );
+		Container c = ( Container ) s.get( Container.class, cOrig.getId()  );
+		assertNotSame( cOrig, c );
+		expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						c, c.getNonLazyInfo(),
+						c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+						c.getNonLazyJoinDataPoints().iterator().next(),
+						c.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		expectedReadOnlyObjects =
+				new HashSet(
+						Arrays.asList( new Object[ ] {
+								c, c.getNoProxyInfo(), c.getProxyInfo(), c.getNonLazyInfo(),
+								c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+								//c.getLazyDataPoints(),
+								c.getNonLazyJoinDataPoints().iterator().next(),
+								c.getNonLazySelectDataPoints().iterator().next()
+						}
+				)
+		);
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		s.setDefaultReadOnly( false );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getNoProxyInfo() ) );
+		Hibernate.initialize( c.getNoProxyInfo() );
+		expectedInitializedObjects.add( c.getNoProxyInfo() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getProxyInfo() ) );
+		Hibernate.initialize( c.getProxyInfo() );
+		expectedInitializedObjects.add( c.getProxyInfo() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getLazyDataPoints() ) );
+		Hibernate.initialize( c.getLazyDataPoints() );
+		expectedInitializedObjects.add( c.getLazyDataPoints().iterator().next() );
+		//expectedReadOnlyObjects.add(c.getLazyDataPoints().iterator().next() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+		s = openSession();
+		t = s.beginTransaction();
+		s.createQuery("delete from DataPoint").executeUpdate();
+		s.createQuery("delete from Container").executeUpdate();
+		s.createQuery("delete from Info").executeUpdate();
+		s.createQuery("delete from Owner").executeUpdate();
+		t.commit();
+		s.close();
+
+	}
+
+	public void testExistingReadOnlyAfterSetSessionModifiableExisting() {
+
+		Container cOrig = createContainer();
+		Set expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						cOrig, cOrig.getNoProxyInfo(), cOrig.getProxyInfo(), cOrig.getNonLazyInfo(),
+						cOrig.getNoProxyOwner(), cOrig.getProxyOwner(), cOrig.getNonLazyOwner(),
+						cOrig.getLazyDataPoints().iterator().next(),
+						cOrig.getNonLazyJoinDataPoints().iterator().next(),
+						cOrig.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		Set expectedReadOnlyObjects = new HashSet();
+		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 );
+		s.setDefaultReadOnly( true );
+		assertTrue( s.isDefaultReadOnly() );
+		checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		s.setDefaultReadOnly( true );
+		Container c = ( Container ) s.get( Container.class, cOrig.getId()  );
+		assertNotSame( cOrig, c );
+		expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						c, c.getNonLazyInfo(),
+						c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+						c.getNonLazyJoinDataPoints().iterator().next(),
+						c.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		expectedReadOnlyObjects =
+				new HashSet(
+						Arrays.asList( new Object[ ] {
+								c, c.getNoProxyInfo(), c.getProxyInfo(), c.getNonLazyInfo(),
+								c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+								//c.getLazyDataPoints(),
+								c.getNonLazyJoinDataPoints().iterator().next(),
+								c.getNonLazySelectDataPoints().iterator().next()
+						}
+				)
+		);
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		s.setDefaultReadOnly( false );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getNoProxyInfo() ) );
+		Hibernate.initialize( c.getNoProxyInfo() );
+		expectedInitializedObjects.add( c.getNoProxyInfo() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getProxyInfo() ) );
+		Hibernate.initialize( c.getProxyInfo() );
+		expectedInitializedObjects.add( c.getProxyInfo() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		DataPoint lazyDataPoint = ( DataPoint ) s.get( DataPoint.class, lazyDataPointOrig.getId() );
+		assertFalse( Hibernate.isInitialized( c.getLazyDataPoints() ) );
+		Hibernate.initialize( c.getLazyDataPoints() );
+		assertSame( lazyDataPoint, c.getLazyDataPoints().iterator().next() );
+		expectedInitializedObjects.add( c.getLazyDataPoints().iterator().next() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+		s = openSession();
+		t = s.beginTransaction();
+		s.createQuery("delete from DataPoint").executeUpdate();
+		s.createQuery("delete from Container").executeUpdate();
+		s.createQuery("delete from Info").executeUpdate();
+		s.createQuery("delete from Owner").executeUpdate();
+		t.commit();
+		s.close();
+
+	}
+
+	public void testExistingReadOnlyAfterSetSessionModifiableExistingEntityReadOnly() {
+
+		Container cOrig = createContainer();
+		Set expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						cOrig, cOrig.getNoProxyInfo(), cOrig.getProxyInfo(), cOrig.getNonLazyInfo(),
+						cOrig.getNoProxyOwner(), cOrig.getProxyOwner(), cOrig.getNonLazyOwner(),
+						cOrig.getLazyDataPoints().iterator().next(),
+						cOrig.getNonLazyJoinDataPoints().iterator().next(),
+						cOrig.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		Set expectedReadOnlyObjects = new HashSet();
+		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 );
+		s.setDefaultReadOnly( true );
+		assertTrue( s.isDefaultReadOnly() );
+		checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		s.setDefaultReadOnly( true );
+		Container c = ( Container ) s.get( Container.class, cOrig.getId()  );
+		assertNotSame( cOrig, c );
+		expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						c, c.getNonLazyInfo(),
+						c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+						c.getNonLazyJoinDataPoints().iterator().next(),
+						c.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		expectedReadOnlyObjects =
+				new HashSet(
+						Arrays.asList( new Object[ ] {
+								c, c.getNoProxyInfo(), c.getProxyInfo(), c.getNonLazyInfo(),
+								c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+								//c.getLazyDataPoints(),
+								c.getNonLazyJoinDataPoints().iterator().next(),
+								c.getNonLazySelectDataPoints().iterator().next()
+						}
+				)
+		);
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		s.setDefaultReadOnly( false );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getNoProxyInfo() ) );
+		Hibernate.initialize( c.getNoProxyInfo() );
+		expectedInitializedObjects.add( c.getNoProxyInfo() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getProxyInfo() ) );
+		Hibernate.initialize( c.getProxyInfo() );
+		expectedInitializedObjects.add( c.getProxyInfo() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		s.setDefaultReadOnly( true );
+		DataPoint lazyDataPoint = ( DataPoint ) s.get( DataPoint.class, lazyDataPointOrig.getId() );
+		s.setDefaultReadOnly( false );
+		assertFalse( Hibernate.isInitialized( c.getLazyDataPoints() ) );
+		Hibernate.initialize( c.getLazyDataPoints() );
+		assertSame( lazyDataPoint, c.getLazyDataPoints().iterator().next() );
+		expectedInitializedObjects.add( c.getLazyDataPoints().iterator().next() );
+		expectedReadOnlyObjects.add( lazyDataPoint );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+		s = openSession();
+		t = s.beginTransaction();
+		s.createQuery("delete from DataPoint").executeUpdate();
+		s.createQuery("delete from Container").executeUpdate();
+		s.createQuery("delete from Info").executeUpdate();
+		s.createQuery("delete from Owner").executeUpdate();
+		t.commit();
+		s.close();
+	}
+
+
+	public void testExistingReadOnlyAfterSetSessionModifiableProxyExisting() {
+
+		Container cOrig = createContainer();
+		Set expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						cOrig, cOrig.getNoProxyInfo(), cOrig.getProxyInfo(), cOrig.getNonLazyInfo(),
+						cOrig.getNoProxyOwner(), cOrig.getProxyOwner(), cOrig.getNonLazyOwner(),
+						cOrig.getLazyDataPoints().iterator().next(),
+						cOrig.getNonLazyJoinDataPoints().iterator().next(),
+						cOrig.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		Set expectedReadOnlyObjects = new HashSet();
+		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 );
+		s.setDefaultReadOnly( true );
+		assertTrue( s.isDefaultReadOnly() );
+		checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		s.setDefaultReadOnly( true );
+		Container c = ( Container ) s.get( Container.class, cOrig.getId()  );
+		assertNotSame( cOrig, c );
+		expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						c, c.getNonLazyInfo(),
+						c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+						c.getNonLazyJoinDataPoints().iterator().next(),
+						c.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		expectedReadOnlyObjects =
+				new HashSet(
+						Arrays.asList( new Object[ ] {
+								c, c.getNoProxyInfo(), c.getProxyInfo(), c.getNonLazyInfo(),
+								c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+								//c.getLazyDataPoints(),
+								c.getNonLazyJoinDataPoints().iterator().next(),
+								c.getNonLazySelectDataPoints().iterator().next()
+						}
+				)
+		);
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		s.setDefaultReadOnly( false );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getNoProxyInfo() ) );
+		Hibernate.initialize( c.getNoProxyInfo() );
+		expectedInitializedObjects.add( c.getNoProxyInfo() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getProxyInfo() ) );
+		Hibernate.initialize( c.getProxyInfo() );
+		expectedInitializedObjects.add( c.getProxyInfo() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		DataPoint lazyDataPoint = ( DataPoint ) s.load( DataPoint.class, lazyDataPointOrig.getId() );
+		assertFalse( Hibernate.isInitialized( c.getLazyDataPoints() ) );
+		Hibernate.initialize( c.getLazyDataPoints() );
+		assertSame( lazyDataPoint, c.getLazyDataPoints().iterator().next() );
+		expectedInitializedObjects.add( c.getLazyDataPoints().iterator().next() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+		s = openSession();
+		t = s.beginTransaction();
+		s.createQuery("delete from DataPoint").executeUpdate();
+		s.createQuery("delete from Container").executeUpdate();
+		s.createQuery("delete from Info").executeUpdate();
+		s.createQuery("delete from Owner").executeUpdate();
+		t.commit();
+		s.close();
+
+	}
+
+	public void testExistingReadOnlyAfterSetSessionModifiableExistingProxyReadOnly() {
+
+		Container cOrig = createContainer();
+		Set expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						cOrig, cOrig.getNoProxyInfo(), cOrig.getProxyInfo(), cOrig.getNonLazyInfo(),
+						cOrig.getNoProxyOwner(), cOrig.getProxyOwner(), cOrig.getNonLazyOwner(),
+						cOrig.getLazyDataPoints().iterator().next(),
+						cOrig.getNonLazyJoinDataPoints().iterator().next(),
+						cOrig.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		Set expectedReadOnlyObjects = new HashSet();
+		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 );
+		s.setDefaultReadOnly( true );
+		assertTrue( s.isDefaultReadOnly() );
+		checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		s.setDefaultReadOnly( true );
+		Container c = ( Container ) s.get( Container.class, cOrig.getId()  );
+		assertNotSame( cOrig, c );
+		expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						c, c.getNonLazyInfo(),
+						c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+						c.getNonLazyJoinDataPoints().iterator().next(),
+						c.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		expectedReadOnlyObjects =
+				new HashSet(
+						Arrays.asList( new Object[ ] {
+								c, c.getNoProxyInfo(), c.getProxyInfo(), c.getNonLazyInfo(),
+								c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+								//c.getLazyDataPoints(),
+								c.getNonLazyJoinDataPoints().iterator().next(),
+								c.getNonLazySelectDataPoints().iterator().next()
+						}
+				)
+		);
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		s.setDefaultReadOnly( false );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getNoProxyInfo() ) );
+		Hibernate.initialize( c.getNoProxyInfo() );
+		expectedInitializedObjects.add( c.getNoProxyInfo() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getProxyInfo() ) );
+		Hibernate.initialize( c.getProxyInfo() );
+		expectedInitializedObjects.add( c.getProxyInfo() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		s.setDefaultReadOnly( true );
+		DataPoint lazyDataPoint = ( DataPoint ) s.load( DataPoint.class, lazyDataPointOrig.getId() );
+		s.setDefaultReadOnly( false );
+		assertFalse( Hibernate.isInitialized( c.getLazyDataPoints() ) );
+		Hibernate.initialize( c.getLazyDataPoints() );
+		assertSame( lazyDataPoint, c.getLazyDataPoints().iterator().next() );
+		expectedInitializedObjects.add( c.getLazyDataPoints().iterator().next() );
+		expectedReadOnlyObjects.add( lazyDataPoint );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+		s = openSession();
+		t = s.beginTransaction();
+		s.createQuery("delete from DataPoint").executeUpdate();
+		s.createQuery("delete from Container").executeUpdate();
+		s.createQuery("delete from Info").executeUpdate();
+		s.createQuery("delete from Owner").executeUpdate();
+		t.commit();
+		s.close();
+	}
+
+	public void testDefaultModifiableWithReadOnlyQueryForEntity() {
+		Container cOrig = createContainer();
+		Set expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						cOrig, cOrig.getNoProxyInfo(), cOrig.getProxyInfo(), cOrig.getNonLazyInfo(),
+						cOrig.getNoProxyOwner(), cOrig.getProxyOwner(), cOrig.getNonLazyOwner(),
+						cOrig.getLazyDataPoints().iterator().next(),
+						cOrig.getNonLazyJoinDataPoints().iterator().next(),
+						cOrig.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		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 );
+		s.setDefaultReadOnly( true );
+		assertTrue( s.isDefaultReadOnly() );
+		checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		assertFalse( s.isDefaultReadOnly() );
+		Container c = ( Container ) s.createQuery( "from Container where id=" + cOrig.getId() )
+				.setReadOnly( true ).uniqueResult();
+		expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						c, c.getNonLazyInfo(),
+						c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+						c.getNonLazyJoinDataPoints().iterator().next(),
+						c.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		expectedReadOnlyObjects =
+				new HashSet(
+						Arrays.asList( new Object[ ] {
+								c, c.getNoProxyInfo(), c.getProxyInfo(), c.getNonLazyInfo(),
+								c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+								//c.getLazyDataPoints(),
+								c.getNonLazyJoinDataPoints().iterator().next(),
+								c.getNonLazySelectDataPoints().iterator().next()
+						}
+				)
+		);
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getNoProxyInfo() ) );
+		Hibernate.initialize( c.getNoProxyInfo() );
+		expectedInitializedObjects.add( c.getNoProxyInfo() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getProxyInfo() ) );
+		Hibernate.initialize( c.getProxyInfo() );
+		expectedInitializedObjects.add( c.getProxyInfo() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getLazyDataPoints() ) );
+		Hibernate.initialize( c.getLazyDataPoints() );
+		expectedInitializedObjects.add( c.getLazyDataPoints().iterator().next() );
+		//expectedReadOnlyObjects.add(c.getLazyDataPoints().iterator().next() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+		s = openSession();
+		t = s.beginTransaction();
+		s.createQuery("delete from DataPoint").executeUpdate();
+		s.createQuery("delete from Container").executeUpdate();
+		s.createQuery("delete from Info").executeUpdate();
+		s.createQuery("delete from Owner").executeUpdate();
+		t.commit();
+		s.close();
+	}
+
+	public void testDefaultReadOnlyWithModifiableQueryForEntity() {
+		Container cOrig = createContainer();
+		Set expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						cOrig, cOrig.getNoProxyInfo(), cOrig.getProxyInfo(), cOrig.getNonLazyInfo(),
+						cOrig.getNoProxyOwner(), cOrig.getProxyOwner(), cOrig.getNonLazyOwner(),
+						cOrig.getLazyDataPoints().iterator().next(),
+						cOrig.getNonLazyJoinDataPoints().iterator().next(),
+						cOrig.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		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 );
+		s.setDefaultReadOnly( true );
+		assertTrue( s.isDefaultReadOnly() );
+		checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		s.setDefaultReadOnly( true );
+		assertTrue( s.isDefaultReadOnly() );
+		Container c = ( Container ) s.createQuery( "from Container where id=" + cOrig.getId() )
+				.setReadOnly( false ).uniqueResult();
+		expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						c, c.getNonLazyInfo(),
+						c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+						c.getNonLazyJoinDataPoints().iterator().next(),
+						c.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		expectedReadOnlyObjects = new HashSet();
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getNoProxyInfo() ) );
+		Hibernate.initialize( c.getNoProxyInfo() );
+		expectedInitializedObjects.add( c.getNoProxyInfo() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getProxyInfo() ) );
+		Hibernate.initialize( c.getProxyInfo() );
+		expectedInitializedObjects.add( c.getProxyInfo() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getLazyDataPoints() ) );
+		Hibernate.initialize( c.getLazyDataPoints() );
+		expectedInitializedObjects.add( c.getLazyDataPoints().iterator().next() );
+		expectedReadOnlyObjects.add(c.getLazyDataPoints().iterator().next() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+		s = openSession();
+		t = s.beginTransaction();
+		s.createQuery("delete from DataPoint").executeUpdate();
+		s.createQuery("delete from Container").executeUpdate();
+		s.createQuery("delete from Info").executeUpdate();
+		s.createQuery("delete from Owner").executeUpdate();
+		t.commit();
+		s.close();
+	}
+
+	public void testDefaultReadOnlyWithQueryForEntity() {
+		Container cOrig = createContainer();
+		Set expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						cOrig, cOrig.getNoProxyInfo(), cOrig.getProxyInfo(), cOrig.getNonLazyInfo(),
+						cOrig.getNoProxyOwner(), cOrig.getProxyOwner(), cOrig.getNonLazyOwner(),
+						cOrig.getLazyDataPoints().iterator().next(),
+						cOrig.getNonLazyJoinDataPoints().iterator().next(),
+						cOrig.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		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 );
+		s.setDefaultReadOnly( true );
+		assertTrue( s.isDefaultReadOnly() );
+		checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		s.setDefaultReadOnly( true );
+		assertTrue( s.isDefaultReadOnly() );
+		Container c = ( Container ) s.createQuery( "from Container where id=" + cOrig.getId() )
+				.uniqueResult();
+		expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						c, c.getNonLazyInfo(),
+						c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+						c.getNonLazyJoinDataPoints().iterator().next(),
+						c.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		expectedReadOnlyObjects =
+				new HashSet(
+						Arrays.asList( new Object[ ] {
+								c, c.getNoProxyInfo(), c.getProxyInfo(), c.getNonLazyInfo(),
+								c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+								//c.getLazyDataPoints(),
+								c.getNonLazyJoinDataPoints().iterator().next(),
+								c.getNonLazySelectDataPoints().iterator().next()
+						}
+				)
+		);
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getNoProxyInfo() ) );
+		Hibernate.initialize( c.getNoProxyInfo() );
+		expectedInitializedObjects.add( c.getNoProxyInfo() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getProxyInfo() ) );
+		Hibernate.initialize( c.getProxyInfo() );
+		expectedInitializedObjects.add( c.getProxyInfo() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getLazyDataPoints() ) );
+		Hibernate.initialize( c.getLazyDataPoints() );
+		expectedInitializedObjects.add( c.getLazyDataPoints().iterator().next() );
+		expectedReadOnlyObjects.add(c.getLazyDataPoints().iterator().next() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+		s = openSession();
+		t = s.beginTransaction();
+		s.createQuery("delete from DataPoint").executeUpdate();
+		s.createQuery("delete from Container").executeUpdate();
+		s.createQuery("delete from Info").executeUpdate();
+		s.createQuery("delete from Owner").executeUpdate();
+		t.commit();
+		s.close();
+	}
+
+	public void testDefaultModifiableWithQueryForEntity() {
+		Container cOrig = createContainer();
+		Set expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						cOrig, cOrig.getNoProxyInfo(), cOrig.getProxyInfo(), cOrig.getNonLazyInfo(),
+						cOrig.getNoProxyOwner(), cOrig.getProxyOwner(), cOrig.getNonLazyOwner(),
+						cOrig.getLazyDataPoints().iterator().next(),
+						cOrig.getNonLazyJoinDataPoints().iterator().next(),
+						cOrig.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		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 );
+		s.setDefaultReadOnly( true );
+		assertTrue( s.isDefaultReadOnly() );
+		checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		assertFalse( s.isDefaultReadOnly() );
+		Container c = ( Container ) s.createQuery( "from Container where id=" + cOrig.getId() )
+				.uniqueResult();
+		expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						c, c.getNonLazyInfo(),
+						c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+						c.getNonLazyJoinDataPoints().iterator().next(),
+						c.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		expectedReadOnlyObjects = new HashSet();
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getNoProxyInfo() ) );
+		Hibernate.initialize( c.getNoProxyInfo() );
+		expectedInitializedObjects.add( c.getNoProxyInfo() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getProxyInfo() ) );
+		Hibernate.initialize( c.getProxyInfo() );
+		expectedInitializedObjects.add( c.getProxyInfo() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		assertFalse( Hibernate.isInitialized( c.getLazyDataPoints() ) );
+		Hibernate.initialize( c.getLazyDataPoints() );
+		expectedInitializedObjects.add( c.getLazyDataPoints().iterator().next() );
+		//expectedReadOnlyObjects.add(c.getLazyDataPoints().iterator().next() );
+		checkContainer( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+		s = openSession();
+		t = s.beginTransaction();
+		s.createQuery("delete from DataPoint").executeUpdate();
+		s.createQuery("delete from Container").executeUpdate();
+		s.createQuery("delete from Info").executeUpdate();
+		s.createQuery("delete from Owner").executeUpdate();
+		t.commit();
+		s.close();
+	}
+
+	public void testDefaultModifiableWithReadOnlyQueryForCollectionEntities() {
+		Container cOrig = createContainer();
+		Set expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						cOrig, cOrig.getNoProxyInfo(), cOrig.getProxyInfo(), cOrig.getNonLazyInfo(),
+						cOrig.getNoProxyOwner(), cOrig.getProxyOwner(), cOrig.getNonLazyOwner(),
+						cOrig.getLazyDataPoints().iterator().next(),
+						cOrig.getNonLazyJoinDataPoints().iterator().next(),
+						cOrig.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		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 );
+		s.setDefaultReadOnly( true );
+		assertTrue( s.isDefaultReadOnly() );
+		checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		assertFalse( s.isDefaultReadOnly() );
+		DataPoint dp = ( DataPoint ) s.createQuery( "select c.lazyDataPoints from Container c join c.lazyDataPoints where c.id=" + cOrig.getId() )
+				.setReadOnly( true ).uniqueResult();
+		assertTrue( s.isReadOnly( dp ) );
+		t.commit();
+		s.close();
+		s = openSession();
+		t = s.beginTransaction();
+		s.createQuery("delete from DataPoint").executeUpdate();
+		s.createQuery("delete from Container").executeUpdate();
+		s.createQuery("delete from Info").executeUpdate();
+		s.createQuery("delete from Owner").executeUpdate();
+		t.commit();
+		s.close();
+	}
+
+	public void testDefaultReadOnlyWithModifiableFilterCollectionEntities() {
+		Container cOrig = createContainer();
+		Set expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						cOrig, cOrig.getNoProxyInfo(), cOrig.getProxyInfo(), cOrig.getNonLazyInfo(),
+						cOrig.getNoProxyOwner(), cOrig.getProxyOwner(), cOrig.getNonLazyOwner(),
+						cOrig.getLazyDataPoints().iterator().next(),
+						cOrig.getNonLazyJoinDataPoints().iterator().next(),
+						cOrig.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		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 );
+		s.setDefaultReadOnly( true );
+		assertTrue( s.isDefaultReadOnly() );
+		checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		s.setDefaultReadOnly( true );
+		assertTrue( s.isDefaultReadOnly() );
+		Container c = ( Container ) s.get( Container.class, cOrig.getId() );
+		assertNotSame( cOrig, c );
+		expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						c, c.getNonLazyInfo(),
+						c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+						c.getNonLazyJoinDataPoints().iterator().next(),
+						c.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		expectedReadOnlyObjects =
+				new HashSet(
+						Arrays.asList( new Object[ ] {
+								c, c.getNoProxyInfo(), c.getProxyInfo(), c.getNonLazyInfo(),
+								c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+								//c.getLazyDataPoints(),
+								c.getNonLazyJoinDataPoints().iterator().next(),
+								c.getNonLazySelectDataPoints().iterator().next()
+						}
+				)
+		);
+		List list = ( List ) s.createFilter( c.getLazyDataPoints(), "" )
+				.setMaxResults(1)
+				.setReadOnly( false )
+				.list();
+		assertEquals( 1, list.size() );
+		assertFalse( s.isReadOnly( list.get( 0 ) ) );
+		list = ( List ) s.createFilter( c.getNonLazyJoinDataPoints(), "" )
+				.setMaxResults(1)
+				.setReadOnly( false )
+				.list();
+		assertEquals( 1, list.size() );
+		assertTrue( s.isReadOnly( list.get( 0 ) ) );
+		list = ( List ) s.createFilter( c.getNonLazySelectDataPoints(), "" )
+				.setMaxResults(1)
+				.setReadOnly( false )
+				.list();
+		assertEquals( 1, list.size() );
+		assertTrue( s.isReadOnly( list.get( 0 ) ) );
+		t.commit();
+		s.close();
+		s = openSession();
+		t = s.beginTransaction();
+		s.createQuery("delete from DataPoint").executeUpdate();
+		s.createQuery("delete from Container").executeUpdate();
+		s.createQuery("delete from Info").executeUpdate();
+		s.createQuery("delete from Owner").executeUpdate();
+		t.commit();
+		s.close();
+	}
+
+	public void testDefaultModifiableWithReadOnlyFilterCollectionEntities() {
+		Container cOrig = createContainer();
+		Set expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						cOrig, cOrig.getNoProxyInfo(), cOrig.getProxyInfo(), cOrig.getNonLazyInfo(),
+						cOrig.getNoProxyOwner(), cOrig.getProxyOwner(), cOrig.getNonLazyOwner(),
+						cOrig.getLazyDataPoints().iterator().next(),
+						cOrig.getNonLazyJoinDataPoints().iterator().next(),
+						cOrig.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		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 );
+		s.setDefaultReadOnly( true );
+		assertTrue( s.isDefaultReadOnly() );
+		checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		assertFalse( s.isDefaultReadOnly() );
+		Container c = ( Container ) s.get( Container.class, cOrig.getId() );
+		assertNotSame( cOrig, c );
+		expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						c, c.getNonLazyInfo(),
+						c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+						c.getNonLazyJoinDataPoints().iterator().next(),
+						c.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		expectedReadOnlyObjects = new HashSet();
+		List list = ( List ) s.createFilter( c.getLazyDataPoints(), "" )
+				.setMaxResults(1)
+				.setReadOnly( true )
+				.list();
+		assertEquals( 1, list.size() );
+		assertTrue( s.isReadOnly( list.get( 0 ) ) );
+		list = ( List ) s.createFilter( c.getNonLazyJoinDataPoints(), "" )
+				.setMaxResults(1)
+				.setReadOnly( true )
+				.list();
+		assertEquals( 1, list.size() );
+		assertFalse( s.isReadOnly( list.get( 0 ) ) );
+		list = ( List ) s.createFilter( c.getNonLazySelectDataPoints(), "" )
+				.setMaxResults(1)
+				.setReadOnly( true )
+				.list();
+		assertEquals( 1, list.size() );
+		assertFalse( s.isReadOnly( list.get( 0 ) ) );
+		t.commit();
+		s.close();
+		s = openSession();
+		t = s.beginTransaction();
+		s.createQuery("delete from DataPoint").executeUpdate();
+		s.createQuery("delete from Container").executeUpdate();
+		s.createQuery("delete from Info").executeUpdate();
+		s.createQuery("delete from Owner").executeUpdate();
+		t.commit();
+		s.close();
+	}
+
+	public void testDefaultReadOnlyWithFilterCollectionEntities() {
+		Container cOrig = createContainer();
+		Set expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						cOrig, cOrig.getNoProxyInfo(), cOrig.getProxyInfo(), cOrig.getNonLazyInfo(),
+						cOrig.getNoProxyOwner(), cOrig.getProxyOwner(), cOrig.getNonLazyOwner(),
+						cOrig.getLazyDataPoints().iterator().next(),
+						cOrig.getNonLazyJoinDataPoints().iterator().next(),
+						cOrig.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		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 );
+		s.setDefaultReadOnly( true );
+		assertTrue( s.isDefaultReadOnly() );
+		checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		s.setDefaultReadOnly( true );
+		assertTrue( s.isDefaultReadOnly() );
+		Container c = ( Container ) s.get( Container.class, cOrig.getId() );
+		assertNotSame( cOrig, c );
+		expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						c, c.getNonLazyInfo(),
+						c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+						c.getNonLazyJoinDataPoints().iterator().next(),
+						c.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		expectedReadOnlyObjects =
+				new HashSet(
+						Arrays.asList( new Object[ ] {
+								c, c.getNoProxyInfo(), c.getProxyInfo(), c.getNonLazyInfo(),
+								c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+								//c.getLazyDataPoints(),
+								c.getNonLazyJoinDataPoints().iterator().next(),
+								c.getNonLazySelectDataPoints().iterator().next()
+						}
+				)
+		);
+		List list = ( List ) s.createFilter( c.getLazyDataPoints(), "" )
+				.setMaxResults(1)
+				.list();
+		assertEquals( 1, list.size() );
+		assertTrue( s.isReadOnly( list.get( 0 ) ) );
+		list = ( List ) s.createFilter( c.getNonLazyJoinDataPoints(), "" )
+				.setMaxResults(1)
+				.list();
+		assertEquals( 1, list.size() );
+		assertTrue( s.isReadOnly( list.get( 0 ) ) );
+		list = ( List ) s.createFilter( c.getNonLazySelectDataPoints(), "" )
+				.setMaxResults(1)
+				.list();
+		assertEquals( 1, list.size() );
+		assertTrue( s.isReadOnly( list.get( 0 ) ) );
+		t.commit();
+		s.close();
+		s = openSession();
+		t = s.beginTransaction();
+		s.createQuery("delete from DataPoint").executeUpdate();
+		s.createQuery("delete from Container").executeUpdate();
+		s.createQuery("delete from Info").executeUpdate();
+		s.createQuery("delete from Owner").executeUpdate();
+		t.commit();
+		s.close();
+	}
+
+	public void testDefaultModifiableWithFilterCollectionEntities() {
+		Container cOrig = createContainer();
+		Set expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						cOrig, cOrig.getNoProxyInfo(), cOrig.getProxyInfo(), cOrig.getNonLazyInfo(),
+						cOrig.getNoProxyOwner(), cOrig.getProxyOwner(), cOrig.getNonLazyOwner(),
+						cOrig.getLazyDataPoints().iterator().next(),
+						cOrig.getNonLazyJoinDataPoints().iterator().next(),
+						cOrig.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		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 );
+		s.setDefaultReadOnly( true );
+		assertTrue( s.isDefaultReadOnly() );
+		checkContainer( cOrig, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		assertFalse( s.isDefaultReadOnly() );
+		Container c = ( Container ) s.get( Container.class, cOrig.getId() );
+		assertNotSame( cOrig, c );
+		expectedInitializedObjects =
+				new HashSet(
+					Arrays.asList( new Object[ ] {
+						c, c.getNonLazyInfo(),
+						c.getNoProxyOwner(), c.getProxyOwner(), c.getNonLazyOwner(),
+						c.getNonLazyJoinDataPoints().iterator().next(),
+						c.getNonLazySelectDataPoints().iterator().next()
+					}
+				)
+		);
+		expectedReadOnlyObjects = new HashSet();
+		List list = ( List ) s.createFilter( c.getLazyDataPoints(), "" )
+				.setMaxResults(1)
+				.list();
+		assertEquals( 1, list.size() );
+		assertFalse( s.isReadOnly( list.get( 0 ) ) );
+		list = ( List ) s.createFilter( c.getNonLazyJoinDataPoints(), "" )
+				.setMaxResults(1)
+				.list();
+		assertEquals( 1, list.size() );
+		assertFalse( s.isReadOnly( list.get( 0 ) ) );
+		list = ( List ) s.createFilter( c.getNonLazySelectDataPoints(), "" )
+				.setMaxResults(1)
+				.list();
+		assertEquals( 1, list.size() );
+		assertFalse( s.isReadOnly( list.get( 0 ) ) );
+		t.commit();
+		s.close();
+		s = openSession();
+		t = s.beginTransaction();
+		s.createQuery("delete from DataPoint").executeUpdate();
+		s.createQuery("delete from Container").executeUpdate();
+		s.createQuery("delete from Info").executeUpdate();
+		s.createQuery("delete from Owner").executeUpdate();
+		t.commit();
+		s.close();
+	}
+
+/*
+	public void testQueryReadOnlyModeExplicitLeftOuterJoinNonLazy() {
+
+		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() )
+				.setReadOnly( true )
+				.list();
+		assertEquals( 1, list.size() );
+		Container c = ( Container ) list.get( 0 );
+		check( c, true, s );
+		check( c.getNoProxyInfo(), false, s );
+		check( c.getProxyInfo(), false, s );
+		check( c.getNonLazyInfo(), true, s );
+
+		//check( c.getNoProxyOwner(), true, s );
+		//check( c.getProxyOwner(), true, s );
+		//check( c.getNonLazyOwner(), true, s );
+		check( c.getLazyDataPoints(), false, s );
+		//check( c.getNonLazyJoinDataPoints(), true, s );
+		//check( c.getNonLazySelectDataPoints(), true, s );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		s.createQuery("delete from DataPoint").executeUpdate();
+		s.createQuery("delete from Container").executeUpdate();
+		s.createQuery("delete from Info").executeUpdate();
+		s.createQuery("delete from Owner").executeUpdate();
+		t.commit();
+		s.close();
+
+	}
+
+	public void testQueryReadOnlyModeExplicitLeftJoinFetchNonLazy() {
+
+		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() )
+				.setReadOnly( true )
+				.list();
+		assertEquals( 1, list.size() );
+		Container c = ( Container ) list.get( 0 );
+		check( c, true, s );
+		check( c.getNoProxyInfo(), false, s );
+		check( c.getProxyInfo(), false, s );
+		check( c.getNonLazyInfo(), true, s );
+
+		// note that Container the following many-to-one properties use a property-ref
+		// so they are initialized, regardless of how the lazy attribute is mapped:
+		// noProxyOwner, proxyOwner, nonLazyOwner
+		//check( c.getNoProxyOwner(), true, s );
+		//check( c.getProxyOwner(), true, s );
+		//check( c.getNonLazyOwner(), true, s );
+		check( c.getLazyDataPoints(), false, s );
+		//check( c.getNonLazyJoinDataPoints(), true, s );
+		//check( c.getNonLazySelectDataPoints(), true, s );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		s.createQuery("delete from DataPoint").executeUpdate();
+		s.createQuery("delete from Container").executeUpdate();
+		s.createQuery("delete from Info").executeUpdate();
+		s.createQuery("delete from Owner").executeUpdate();
+		t.commit();
+		s.close();
+
+	}
+
+	public void testEntityReadOnlyMode() {
+
+		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 );
+		s.setReadOnly( c, true );
+		check( c, true, s );
+		check( c.getNoProxyInfo(), false, s );
+		check( c.getProxyInfo(), false, s );
+		check( c.getNonLazyInfo(), true, s );
+
+		// note that Container the following many-to-one properties use a property-ref
+		// so they are initialized, regardless of how the lazy attribute is mapped:
+		// noProxyOwner, proxyOwner, nonLazyOwner
+		check( c.getNoProxyOwner(), true, s );
+		check( c.getProxyOwner(), true, s );
+		check( c.getNonLazyOwner(), true, s );
+		check( c.getLazyDataPoints(), false, s );
+		check( c.getNonLazyJoinDataPoints(), true, s );
+		check( c.getNonLazySelectDataPoints(), true, s );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		s.createQuery("delete from DataPoint").executeUpdate();
+		s.createQuery("delete from Container").executeUpdate();
+		s.createQuery("delete from Info").executeUpdate();
+		s.createQuery("delete from Owner").executeUpdate();
+		t.commit();
+		s.close();
+
+	}
+*/
+
+	private Container createContainer() {
+		Container c = new Container( "container" );
+		c.setNoProxyInfo( new Info( "no-proxy info" ) );
+		c.setProxyInfo( new Info( "proxy info" ) );
+		c.setNonLazyInfo( new Info( "non-lazy info" ) );
+		c.setNoProxyOwner( new Owner( "no-proxy owner" ) );
+		c.setProxyOwner( new Owner( "proxy owner" ) );
+		c.setNonLazyOwner( new Owner( "non-lazy owner" ) );
+		c.getLazyDataPoints().add( new DataPoint( new BigDecimal( 1 ), new BigDecimal( 1 ), "lazy data point" ) );
+		c.getNonLazyJoinDataPoints().add( new DataPoint( new BigDecimal( 2 ), new BigDecimal( 2 ), "non-lazy join data point" ) );
+		c.getNonLazySelectDataPoints().add( new DataPoint( new BigDecimal( 3 ), new BigDecimal( 3 ), "non-lazy select data point" ) );
+		return c;
+	}
+
+	private void checkContainer(Container c, Set expectedInitializedObjects, Set expectedReadOnlyObjects, Session s) {
+		checkObject( c, expectedInitializedObjects, expectedReadOnlyObjects, s );
+		if ( ! expectedInitializedObjects.contains( c ) ) {
+			return;
+		}
+		checkObject( c.getNoProxyInfo(), expectedInitializedObjects, expectedReadOnlyObjects, s);
+		checkObject( c.getProxyInfo(), expectedInitializedObjects, expectedReadOnlyObjects, s);
+		checkObject( c.getNonLazyInfo(), expectedInitializedObjects, expectedReadOnlyObjects, s );
+		checkObject( c.getNoProxyOwner(), expectedInitializedObjects, expectedReadOnlyObjects, s );
+		checkObject( c.getProxyOwner(), expectedInitializedObjects, expectedReadOnlyObjects, s );
+		checkObject( c.getNonLazyOwner(), expectedInitializedObjects, expectedReadOnlyObjects, s );
+		if ( Hibernate.isInitialized( c.getLazyDataPoints() ) ) {
+			for ( Iterator it=c.getLazyDataPoints().iterator(); it.hasNext(); ) {
+				checkObject( it.next(), expectedInitializedObjects, expectedReadOnlyObjects, s );
+			}
+		}
+		for ( Iterator it=c.getNonLazyJoinDataPoints().iterator(); it.hasNext(); ) {
+			checkObject( it.next(), expectedInitializedObjects, expectedReadOnlyObjects, s );
+		}
+		for ( Iterator it=c.getNonLazySelectDataPoints().iterator(); it.hasNext(); ) {
+			checkObject( it.next(), expectedInitializedObjects, expectedReadOnlyObjects, s );
+		}
+	}
+
+	private void checkObject(Object entityOrProxy, Set expectedInitializedObjects, Set expectedReadOnlyObjects, Session s) {
+		boolean isExpectedToBeInitialized = expectedInitializedObjects.contains( entityOrProxy );
+		boolean isExpectedToBeReadOnly = expectedReadOnlyObjects.contains( entityOrProxy );
+		SessionImplementor si = ( SessionImplementor ) s;
+		assertEquals( isExpectedToBeInitialized, Hibernate.isInitialized( entityOrProxy ) );
+		assertEquals( isExpectedToBeReadOnly, s.isReadOnly( entityOrProxy ) );
+		if ( Hibernate.isInitialized( entityOrProxy ) ) {
+			Object entity = ( entityOrProxy instanceof HibernateProxy ?
+					( ( HibernateProxy ) entityOrProxy ).getHibernateLazyInitializer().getImplementation( si ) :
+					entityOrProxy
+			);
+			assertNotNull( entity );
+			assertEquals( isExpectedToBeReadOnly, s.isReadOnly( entity ));
+		}
+	}
+
+}
\ No newline at end of file

Added: core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlySessionTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlySessionTest.java	                        (rev 0)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlySessionTest.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -0,0 +1,456 @@
+//$Id: ReadOnlyTest.java 10977 2006-12-12 23:28:04Z steve.ebersole at 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;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.hibernate.CacheMode;
+import org.hibernate.Hibernate;
+import org.hibernate.ScrollMode;
+import org.hibernate.ScrollableResults;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ *
+ * @author Gail Badner
+ */
+public class ReadOnlySessionTest extends FunctionalTestCase {
+
+	public ReadOnlySessionTest(String str) {
+		super(str);
+	}
+
+	public String[] getMappings() {
+		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 );
+	}
+
+	public void testReadOnlyOnProxies() {
+		Session s = openSession();
+		s.setCacheMode( CacheMode.IGNORE );
+		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) );
+		dp.setDescription( "original" );
+		s.save( dp );
+		long dpId = dp.getId();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		s.setDefaultReadOnly( true );
+		assertTrue( s.isDefaultReadOnly() );
+		dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpId ) );
+		s.setDefaultReadOnly( false );
+		assertFalse( "was initialized", Hibernate.isInitialized( dp ) );
+		assertTrue( s.isReadOnly( dp ) );
+		assertFalse( "was initialized during isReadOnly", Hibernate.isInitialized( dp ) );
+		dp.setDescription( "changed" );
+		assertTrue( "was not initialized during mod", Hibernate.isInitialized( dp ) );
+		assertEquals( "desc not changed in memory", "changed", dp.getDescription() );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		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();
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testReadOnlyMode() {
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		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) );
+			dp.setY( new BigDecimal( Math.cos( dp.getX().doubleValue() ) ).setScale(19, BigDecimal.ROUND_DOWN) );
+			s.save(dp);
+		}
+		t.commit();
+		s.close();
+
+		s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		t = s.beginTransaction();
+		s.setDefaultReadOnly( true );
+		int i = 0;
+		ScrollableResults sr = s.createQuery("from DataPoint dp order by dp.x asc")
+				.scroll(ScrollMode.FORWARD_ONLY);
+		while ( sr.next() ) {
+			DataPoint dp = (DataPoint) sr.get(0);
+			if (++i==50) {
+				s.setReadOnly(dp, false);
+			}
+			dp.setDescription("done!");
+		}
+		t.commit();
+		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();
+		t.commit();
+		s.close();
+	}
+
+	public void testReadOnlyQueryScrollChangeToModifiableBeforeIterate() {
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		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) );
+			dp.setY( new BigDecimal( Math.cos( dp.getX().doubleValue() ) ).setScale(19, BigDecimal.ROUND_DOWN) );
+			s.save(dp);
+		}
+		t.commit();
+		s.close();
+
+		s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		t = s.beginTransaction();
+		s.setDefaultReadOnly( true );
+		int i = 0;
+		ScrollableResults sr = s.createQuery("from DataPoint dp order by dp.x asc")
+				.scroll(ScrollMode.FORWARD_ONLY);
+		s.setDefaultReadOnly( false );
+		while ( sr.next() ) {
+			DataPoint dp = (DataPoint) sr.get(0);
+			if (++i==50) {
+				s.setReadOnly(dp, false);
+			}
+			dp.setDescription("done!");
+		}
+		t.commit();
+		s.clear();
+		t = s.beginTransaction();
+		List single = s.createQuery("from DataPoint where description='done!'").list();
+		assertEquals( 1, single.size() );
+		s.createQuery("delete from DataPoint").executeUpdate();
+		t.commit();
+		s.close();
+	}
+
+	public void testReadOnlyRefreshFailureExpected() {
+
+		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);
+		t.commit();
+		s.close();
+
+		s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.setDefaultReadOnly( true );
+		t = s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dp.getId() );
+		assertEquals( "original", dp.getDescription() );
+		dp.setDescription( "changed" );
+		assertEquals( "changed", dp.getDescription() );
+		s.setDefaultReadOnly( false );
+		s.refresh( dp );
+		assertEquals( "original", dp.getDescription() );
+		dp.setDescription( "changed" );
+		assertEquals( "changed", dp.getDescription() );
+		t.commit();
+
+		s.clear();
+		t = s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dp.getId() );
+		assertEquals( "original", dp.getDescription() );
+		s.delete( dp );
+		t.commit();
+		s.close();
+
+	}
+
+	public void testReadOnlyDelete() {
+
+		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();
+
+		s = openSession();
+		s.setDefaultReadOnly( true );
+		s.setCacheMode(CacheMode.IGNORE);
+		t = s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dp.getId() );
+		s.setDefaultReadOnly( false );
+		assertTrue( s.isReadOnly( dp ) );
+		s.delete(  dp );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		List list = s.createQuery("from DataPoint where id=" + dp.getId() ).list();
+		assertTrue( list.isEmpty() );
+		t.commit();
+		s.close();
+
+	}
+
+	public void testReadOnlyGetModifyAndDelete() {
+
+		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();
+
+		s = openSession();
+		s.setDefaultReadOnly( true );
+		s.setCacheMode(CacheMode.IGNORE);
+		t = s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dp.getId() );
+		s.setDefaultReadOnly( true );
+		dp.setDescription( "a DataPoint" );
+		s.delete(  dp );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		List list = s.createQuery("from DataPoint where id=" + dp.getId() ).list();
+		assertTrue( list.isEmpty() );
+		t.commit();
+		s.close();
+
+	}
+
+	public void testReadOnlyModeWithExistingModifiableEntity() {
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		Transaction t = s.beginTransaction();
+		DataPoint dp = null;
+		for ( int i=0; i<100; i++ ) {
+			dp = new DataPoint();
+			dp.setX( new BigDecimal(i * 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();
+
+		s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		t = s.beginTransaction();
+		DataPoint dpLast = ( DataPoint ) s.get( DataPoint.class,  dp.getId() );
+		assertFalse( s.isReadOnly( dpLast ) );
+		s.setDefaultReadOnly( true );
+		int i = 0;
+		ScrollableResults sr = s.createQuery("from DataPoint dp order by dp.x asc")
+				.scroll(ScrollMode.FORWARD_ONLY);
+		s.setDefaultReadOnly( false );
+		int nExpectedChanges = 0;
+		while ( sr.next() ) {
+			dp = (DataPoint) sr.get(0);
+			if ( dp.getId() == dpLast.getId() ) {
+				//dpLast existed in the session before executing the read-only query
+				assertFalse( s.isReadOnly( dp ) );
+			}
+			else {
+				assertTrue( s.isReadOnly( dp ) );
+			}
+			if (++i==50) {
+				s.setReadOnly(dp, false);
+				nExpectedChanges = ( dp == dpLast ? 1 : 2 );
+			}
+			dp.setDescription("done!");
+		}
+		t.commit();
+		s.clear();
+		t = s.beginTransaction();
+		List list = s.createQuery("from DataPoint where description='done!'").list();
+		assertEquals( nExpectedChanges, list.size() );
+		s.createQuery("delete from DataPoint").executeUpdate();
+		t.commit();
+		s.close();
+	}
+
+	public void testModifiableModeWithExistingReadOnlyEntity() {
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		Transaction t = s.beginTransaction();
+		DataPoint dp = null;
+		for ( int i=0; i<100; i++ ) {
+			dp = new DataPoint();
+			dp.setX( new BigDecimal(i * 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();
+
+		s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		t = s.beginTransaction();
+		s.setDefaultReadOnly( true );
+		DataPoint dpLast = ( DataPoint ) s.get( DataPoint.class,  dp.getId() );
+		assertTrue( s.isReadOnly( dpLast ) );
+		int i = 0;
+		ScrollableResults sr = s.createQuery("from DataPoint dp order by dp.x asc")
+				.setReadOnly(false)
+				.scroll(ScrollMode.FORWARD_ONLY);
+		int nExpectedChanges = 0;
+		while ( sr.next() ) {
+			dp = (DataPoint) sr.get(0);
+			if ( dp.getId() == dpLast.getId() ) {
+				//dpLast existed in the session before executing the read-only query
+				assertTrue( s.isReadOnly( dp ) );
+			}
+			else {
+				assertFalse( s.isReadOnly( dp ) );
+			}
+			if (++i==50) {
+				s.setReadOnly(dp, true);
+				nExpectedChanges = ( dp == dpLast ? 99 : 98 );
+			}
+			dp.setDescription("done!");
+		}
+		t.commit();
+		s.clear();
+		t = s.beginTransaction();
+		List list = s.createQuery("from DataPoint where description='done!'").list();
+		assertEquals( nExpectedChanges, list.size() );
+		s.createQuery("delete from DataPoint").executeUpdate();
+		t.commit();
+		s.close();
+	}
+
+	public void testReadOnlyOnTextType() {
+		final String origText = "some huge text string";
+		final String newText = "some even bigger text string";
+
+		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();
+
+		s = openSession();
+		s.beginTransaction();
+		s.setDefaultReadOnly( true );
+		s.setCacheMode( CacheMode.IGNORE );
+		holder = ( TextHolder ) s.get( TextHolder.class, id );
+		s.setDefaultReadOnly( false );
+		holder.setTheText( newText );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		holder = ( TextHolder ) s.get( TextHolder.class, id );
+		assertEquals( "change written to database", origText, holder.getTheText() );
+		s.delete( holder );
+		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();
+		s.setDefaultReadOnly( true );
+		DataPoint dpManaged = ( DataPoint ) s.get( DataPoint.class, new Long( dp.getId() ) );
+		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();
+
+	}
+}
\ No newline at end of file

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java	2010-01-26 21:49:51 UTC (rev 18640)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java	2010-01-27 06:36:55 UTC (rev 18641)
@@ -26,6 +26,7 @@
 package org.hibernate.test.readonly;
 
 import java.math.BigDecimal;
+import java.util.Iterator;
 import java.util.List;
 
 import junit.framework.Test;
@@ -44,6 +45,7 @@
 /**
  * 
  * @author Gavin King
+ * @author Gail Badner
  */
 public class ReadOnlyTest extends FunctionalTestCase {
 	
@@ -142,6 +144,31 @@
 		
 	}
 
+	public void testReadOnlyModeAutoFlushOnQuery() {
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		Transaction t = s.beginTransaction();
+		DataPoint dpFirst = null;
+		for ( int i=0; i<100; i++ ) {
+			DataPoint dp = new DataPoint();
+			dp.setX( new BigDecimal(i * 0.1d).setScale(19, BigDecimal.ROUND_DOWN) );
+			dp.setY( new BigDecimal( Math.cos( dp.getX().doubleValue() ) ).setScale(19, BigDecimal.ROUND_DOWN) );
+			s.save(dp);
+		}
+		ScrollableResults sr = s.createQuery("from DataPoint dp order by dp.x asc")
+				.setReadOnly(true)
+				.scroll(ScrollMode.FORWARD_ONLY);
+		while ( sr.next() ) {
+			DataPoint dp = (DataPoint) sr.get(0);
+			assertFalse( s.isReadOnly( dp ) );
+			s.delete( dp );
+		}
+		t.commit();
+		s.close();
+
+	}
+
 	public void testReadOnlyRefreshFailureExpected() {
 
 		Session s = openSession();
@@ -209,6 +236,37 @@
 
 	}
 
+	public void testReadOnlyGetModifyAndDelete() {
+
+		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();
+
+		s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		t = s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dp.getId() );
+		s.setReadOnly( dp, true );
+		dp.setDescription( "a DataPoint" );
+		s.delete(  dp );
+		t.commit();
+		s.close();
+
+		s = openSession();
+		t = s.beginTransaction();
+		List list = s.createQuery("from DataPoint where description='done!'").list();
+		assertTrue( list.isEmpty() );
+		t.commit();
+		s.close();
+
+	}
+
 	public void testReadOnlyModeWithExistingModifiableEntity() {
 
 		Session s = openSession();



More information about the hibernate-commits mailing list