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

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Tue Jan 12 20:04:56 EST 2010


Author: gbadner
Date: 2010-01-12 20:04:55 -0500 (Tue, 12 Jan 2010)
New Revision: 18525

Added:
   core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyProxyTest.java
Modified:
   core/trunk/core/src/main/java/org/hibernate/Session.java
   core/trunk/core/src/main/java/org/hibernate/engine/EntityEntry.java
   core/trunk/core/src/main/java/org/hibernate/engine/PersistenceContext.java
   core/trunk/core/src/main/java/org/hibernate/engine/StatefulPersistenceContext.java
   core/trunk/core/src/main/java/org/hibernate/engine/TwoPhaseLoad.java
   core/trunk/core/src/main/java/org/hibernate/impl/SessionImpl.java
   core/trunk/core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java
   core/trunk/core/src/main/java/org/hibernate/proxy/LazyInitializer.java
   core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java
Log:
Session.setReadOnly(Object, boolean) fails for proxies

Modified: core/trunk/core/src/main/java/org/hibernate/Session.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/Session.java	2010-01-12 21:00:11 UTC (rev 18524)
+++ core/trunk/core/src/main/java/org/hibernate/Session.java	2010-01-13 01:04:55 UTC (rev 18525)
@@ -839,15 +839,29 @@
 	 * Get the statistics for this session.
 	 */
 	public SessionStatistics getStatistics();
-	
+
 	/**
-	 * 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
+	 * Is the specified entity or proxy read-only?
+	 * @param entityOrProxy, an entity or HibernateProxy
+	 * @return true, the entity or proxy is read-only;
+	 *         false, the entity or proxy is modifiable.
+	 */
+	public boolean isReadOnly(Object entityOrProxy);
+
+	/**
+	 * 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.
+	 *
+	 * If the entity or proxy already has the specified read-only/modifiable
+	 * setting, then this method does nothing.
 	 * 
+	 * @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 entity, boolean readOnly);
+	public void setReadOnly(Object entityOrProxy, boolean readOnly);
 
 	/**
 	 * Controller for allowing users to perform JDBC related work using the Connection

Modified: core/trunk/core/src/main/java/org/hibernate/engine/EntityEntry.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/engine/EntityEntry.java	2010-01-12 21:00:11 UTC (rev 18524)
+++ core/trunk/core/src/main/java/org/hibernate/engine/EntityEntry.java	2010-01-13 01:04:55 UTC (rev 18525)
@@ -261,22 +261,25 @@
 		);
 	}
 
+	public boolean isReadOnly() {
+		if (status != Status.MANAGED && status != Status.READ_ONLY) {
+			throw new HibernateException("instance was not in a valid state");
+		}
+		return status == Status.READ_ONLY;
+	}
+
 	public void setReadOnly(boolean readOnly, Object entity) {
-		if ( ( readOnly && status == Status.READ_ONLY ) ||
-			 ( ( ! readOnly ) && status == Status.MANAGED ) ) {
+		if ( readOnly == isReadOnly() ) {
 			// simply return since the status is not being changed
 			return;
 		}
-		if (status!=Status.MANAGED && status!=Status.READ_ONLY) {
-			throw new HibernateException("instance was not in a valid state");
-		}
-		if (readOnly) {
-			setStatus(Status.READ_ONLY);
+		if ( readOnly ) {
+			setStatus( Status.READ_ONLY );
 			loadedState = null;
 		}
 		else {
-			setStatus(Status.MANAGED);
-			loadedState = getPersister().getPropertyValues(entity, entityMode);
+			setStatus( Status.MANAGED );
+			loadedState = getPersister().getPropertyValues( entity, entityMode );
 		}
 	}
 	

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-12 21:00:11 UTC (rev 18524)
+++ core/trunk/core/src/main/java/org/hibernate/engine/PersistenceContext.java	2010-01-13 01:04:55 UTC (rev 18525)
@@ -475,12 +475,27 @@
 	 * Is the association property belonging to the keyed entity null?
 	 */
 	public boolean isPropertyNull(EntityKey ownerKey, String propertyName);
-	
+
 	/**
-	 * Set the object to read only and discard it's snapshot
+	 * Is the entity or proxy read-only?
+	 *
+	 * @param entityOrProxy
+	 * @return true, the object is read-only; false, the object is modifiable.
 	 */
-	public void setReadOnly(Object entity, boolean readOnly);
+	public boolean isReadOnly(Object entityOrProxy);
 
+	/**
+	 * Set the entity or proxy to read only and discard it's snapshot.
+	 *
+	 * If the entity or proxy already has the specified read-only/modifiable
+	 * setting, then this method does nothing.
+	 *
+	 * @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.
+	 */
+	public void setReadOnly(Object entityOrProxy, boolean readOnly);
+
 	void replaceDelayedEntityIdentityInsertKeys(EntityKey oldKey, Serializable generatedId);
 
 	/**

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-12 21:00:11 UTC (rev 18524)
+++ core/trunk/core/src/main/java/org/hibernate/engine/StatefulPersistenceContext.java	2010-01-13 01:04:55 UTC (rev 18525)
@@ -1302,13 +1302,67 @@
 		nullAssociations.clear();
 	}
 
-	public void setReadOnly(Object entity, boolean readOnly) {
+	public boolean isReadOnly(Object entityOrProxy) {
+		if ( entityOrProxy == null ) {
+			throw new AssertionFailure( "object must be non-null." );
+		}
+		boolean isReadOnly;
+		if ( entityOrProxy instanceof HibernateProxy ) {
+			isReadOnly = ( ( HibernateProxy ) entityOrProxy ).getHibernateLazyInitializer().isReadOnly();
+		}
+		else {
+			EntityEntry ee =  getEntry( entityOrProxy );
+			if ( ee == null ) {
+				throw new TransientObjectException("Instance was not associated with this persistence context" );
+			}
+			isReadOnly = ee.isReadOnly();
+		}
+		return isReadOnly;
+	}
+
+	public void setReadOnly(Object object, boolean readOnly) {
+		if ( object == null ) {
+			throw new AssertionFailure( "object must be non-null." );
+		}
+		if ( isReadOnly( object ) == readOnly ) {
+			return;
+		}
+		if ( object instanceof HibernateProxy ) {
+			HibernateProxy proxy = ( HibernateProxy ) object;
+			setProxyReadOnly( proxy, readOnly );
+			if ( Hibernate.isInitialized( proxy ) ) {
+				setEntityReadOnly(
+						proxy.getHibernateLazyInitializer().getImplementation(),
+						readOnly
+				);
+			}
+		}
+		else {
+			setEntityReadOnly( object, readOnly );
+			// PersistenceContext.proxyFor( entity ) returns entity if there is no proxy for that entity
+			// so need to check the return value to be sure it is really a proxy
+			Object maybeProxy = getSession().getPersistenceContext().proxyFor( object );
+			if ( maybeProxy instanceof HibernateProxy ) {
+				setProxyReadOnly( ( HibernateProxy ) maybeProxy, readOnly );
+			}
+		}
+	}
+
+	private void setProxyReadOnly(HibernateProxy proxy, boolean readOnly) {
+		if ( proxy.getHibernateLazyInitializer().getSession() != getSession() ) {
+			throw new AssertionFailure(
+					"Attempt to set a proxy to read-only that is associated with a different session" );
+		}
+		proxy.getHibernateLazyInitializer().setReadOnly( readOnly );
+	}
+
+	private void setEntityReadOnly(Object entity, boolean readOnly) {
 		EntityEntry entry = getEntry(entity);
-		if (entry==null) {
-			throw new TransientObjectException("Instance was not associated with the session");
+		if (entry == null) {
+			throw new TransientObjectException("Instance was not associated with this persistence context" );
 		}
-		entry.setReadOnly(readOnly, entity);
-		hasNonReadOnlyEntities = hasNonReadOnlyEntities || !readOnly;
+		entry.setReadOnly(readOnly, entity );
+		hasNonReadOnlyEntities = hasNonReadOnlyEntities || ! readOnly;
 	}
 
 	public void replaceDelayedEntityIdentityInsertKeys(EntityKey oldKey, Serializable generatedId) {

Modified: core/trunk/core/src/main/java/org/hibernate/engine/TwoPhaseLoad.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/engine/TwoPhaseLoad.java	2010-01-12 21:00:11 UTC (rev 18524)
+++ core/trunk/core/src/main/java/org/hibernate/engine/TwoPhaseLoad.java	2010-01-13 01:04:55 UTC (rev 18525)
@@ -32,6 +32,7 @@
 import org.hibernate.CacheMode;
 import org.hibernate.HibernateException;
 import org.hibernate.LockMode;
+import org.hibernate.proxy.HibernateProxy;
 import org.hibernate.cache.CacheKey;
 import org.hibernate.cache.entry.CacheEntry;
 import org.hibernate.event.PostLoadEvent;
@@ -188,8 +189,18 @@
 				factory.getStatisticsImplementor().secondLevelCachePut( persister.getCacheAccessStrategy().getRegion().getName() );
 			}
 		}
-	
-		if ( readOnly || !persister.isMutable() ) {
+
+		boolean isReallyReadOnly = readOnly || !persister.isMutable();
+		Object proxy = persistenceContext.getProxy(
+				new EntityKey(entityEntry.getId(), entityEntry.getPersister(), session.getEntityMode()
+			)
+		);
+		if ( proxy != null ) {
+			// there is already a proxy for this impl
+			// only set the status to read-only if the proxy is read-only
+			isReallyReadOnly = ( ( HibernateProxy ) proxy ).getHibernateLazyInitializer().isReadOnly();
+		}
+		if ( isReallyReadOnly ) {
 			//no need to take a snapshot - this is a 
 			//performance optimization, but not really
 			//important, except for entities with huge 

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-12 21:00:11 UTC (rev 18524)
+++ core/trunk/core/src/main/java/org/hibernate/impl/SessionImpl.java	2010-01-13 01:04:55 UTC (rev 18525)
@@ -1956,6 +1956,12 @@
 		return true;
 	}
 
+	public boolean isReadOnly(Object entityOrProxy) {
+		errorIfClosed();
+		checkTransactionSynchStatus();
+		return persistenceContext.isReadOnly( entityOrProxy );	
+	}
+
 	public void setReadOnly(Object entity, boolean readOnly) {
 		errorIfClosed();
 		checkTransactionSynchStatus();

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-12 21:00:11 UTC (rev 18524)
+++ core/trunk/core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java	2010-01-13 01:04:55 UTC (rev 18525)
@@ -28,6 +28,8 @@
 
 import org.hibernate.HibernateException;
 import org.hibernate.LazyInitializationException;
+import org.hibernate.TransientObjectException;
+import org.hibernate.SessionException;
 import org.hibernate.engine.EntityKey;
 import org.hibernate.engine.SessionImplementor;
 
@@ -43,8 +45,8 @@
 	private Serializable id;
 	private Object target;
 	private boolean initialized;
+	private boolean readOnly;
 	private boolean unwrap;
-
 	private transient SessionImplementor session;
 
 	/**
@@ -63,7 +65,15 @@
 	protected AbstractLazyInitializer(String entityName, Serializable id, SessionImplementor session) {
 		this.entityName = entityName;
 		this.id = id;
-		this.session = session;
+		// 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 );
+		}
 	}
 
 	/**
@@ -108,7 +118,9 @@
 		if ( s != session ) {
 			// check for s == null first, since it is least expensive
 			if ( s == null ){
-				unsetSession();
+				// 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...
@@ -116,6 +128,8 @@
 			}
 			else {
 				session = s;
+				// NOTE: the proxy may not be connected to the session yet, so set readOnly directly
+				readOnly = ! session.getFactory().getEntityPersister( entityName ).isMutable();
 			}
 		}
 	}
@@ -132,6 +146,7 @@
 	 */
 	public void unsetSession() {
 		session = null;
+		readOnly = false;
 	}
 
 	/**
@@ -223,6 +238,48 @@
 	/**
 	 * {@inheritDoc}
 	 */
+	public boolean isReadOnly() {
+		errorIfReadOnlySettingNotAvailable();
+		if ( !isConnectedToSession() ) {
+			throw new TransientObjectException(
+					"The read-only/modifiable setting is only accessible when the proxy is associated with a session." );
+		}
+		return readOnly;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setReadOnly(boolean readOnly) {
+		errorIfReadOnlySettingNotAvailable();
+		// only update if readOnly is different from current setting
+		if ( this.readOnly != readOnly ) {
+			Object proxy = getProxyOrNull();
+			if ( proxy == null ) {
+				throw new TransientObjectException(
+						"Cannot set the read-only/modifiable mode unless the proxy is associated with a session." );
+			}
+			this.readOnly = readOnly;
+			if ( initialized ) {
+				session.getPersistenceContext().setReadOnly( target, readOnly );
+			}
+		}
+	}
+
+	private void errorIfReadOnlySettingNotAvailable() {
+		if ( session == null ) {
+			throw new TransientObjectException(
+					"Proxy is detached (i.e, session is null). The read-only/modifiable setting is only accessible when the proxy is associated with an open session." );
+		}
+		if ( session.isClosed() ) {
+			throw new SessionException(
+					"Session is closed. The read-only/modifiable setting is only accessible when the proxy is associated with an open session." );
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
 	public boolean isUnwrap() {
 		return unwrap;
 	}

Modified: core/trunk/core/src/main/java/org/hibernate/proxy/LazyInitializer.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/proxy/LazyInitializer.java	2010-01-12 21:00:11 UTC (rev 18524)
+++ core/trunk/core/src/main/java/org/hibernate/proxy/LazyInitializer.java	2010-01-13 01:04:55 UTC (rev 18525)
@@ -105,6 +105,37 @@
 	public void setImplementation(Object target);
 
 	/**
+	 * Is the proxy read-only?.
+	 *
+	 * @return true, if this proxy is read-only; false, otherwise
+	 * @throws org.hibernate.TransientObjectException if the proxy is not association with a session
+	 * @throws org.hibernate.SessionException if the proxy is associated with a sesssion that is closed
+	 *
+	 * @see org.hibernate.Session#isReadOnly(Object entityOrProxy)
+	 */
+	public boolean isReadOnly();
+
+	/**
+	 * Set an associated modifiable proxy to read-only mode, or a read-only
+	 * proxy to modifiable mode. If the proxy is currently initialized, its
+	 * implementation will be set to the same mode; otherwise, when the
+	 * proxy is initialized, its implementation will have the same read-only/
+	 * modifiable setting as the proxy. In read-only mode, no snapshot is
+	 * maintained and the instance is never dirty checked.
+	 *
+	 * If the associated proxy already has the specified read-only/modifiable
+	 * setting, then this method does nothing.
+	 *
+	 * @param readOnly, if true, the associated proxy is made read-only;
+	 *                  if false, the associated proxy is made modifiable.
+	 * @throws org.hibernate.TransientObjectException if the proxy is not association with a session
+	 * @throws org.hibernate.SessionException if the proxy is associated with a sesssion that is closed
+	 * 
+	 * @see org.hibernate.Session#setReadOnly(Object entityOrProxy, boolean readOnly)
+	 */
+	public void setReadOnly(boolean readOnly);
+
+	/**
 	 * Get the session to which this proxy is associated, or null if it is not attached.
 	 *
 	 * @return The associated session.

Added: core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyProxyTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyProxyTest.java	                        (rev 0)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyProxyTest.java	2010-01-13 01:04:55 UTC (rev 18525)
@@ -0,0 +1,1491 @@
+//$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.TransientObjectException;
+import org.hibernate.SessionException;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.proxy.LazyInitializer;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * Tests making initialized and uninitialized proxies read-only/modifiable
+ * 
+ * @author Gail Badner
+ */
+public class ReadOnlyProxyTest extends FunctionalTestCase {
+
+	public ReadOnlyProxyTest(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( ReadOnlyProxyTest.class );
+	}
+
+	public void testReadOnlyViaSessionDoesNotInit() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		checkReadOnly( s, dp, false );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		s.setReadOnly( dp, true );
+		checkReadOnly( s, dp, true );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		s.setReadOnly( dp, false );
+		checkReadOnly( s, dp, false );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		s.flush();
+		checkReadOnly( s, dp, false );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		s.getTransaction().commit();
+		checkReadOnly( s, dp, false );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( dpOrig.getDescription(), dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testReadOnlyViaLazyInitializerDoesNotInit() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		LazyInitializer dpLI = ( ( HibernateProxy ) dp ).getHibernateLazyInitializer();
+		checkReadOnly( s, dp, false );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		dpLI.setReadOnly( true );
+		checkReadOnly( s, dp, true );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		dpLI.setReadOnly( false );
+		checkReadOnly( s, dp, false );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		s.flush();
+		checkReadOnly( s, dp, false );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		s.getTransaction().commit();
+		checkReadOnly( s, dp, false );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( dpOrig.getDescription(), dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testReadOnlyViaSessionNoChangeAfterInit() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		checkReadOnly( s, dp, false );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		Hibernate.initialize( dp );
+		assertTrue( Hibernate.isInitialized( dp ) );
+		checkReadOnly( s, dp, false );
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		s.setReadOnly( dp, true );
+		checkReadOnly( s, dp, true );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		Hibernate.initialize( dp );
+		assertTrue( Hibernate.isInitialized( dp ) );
+		checkReadOnly( s, dp, true );
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		s.setReadOnly( dp, true );
+		checkReadOnly( s, dp, true );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		s.setReadOnly( dp, false );
+		checkReadOnly( s, dp, false );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		Hibernate.initialize( dp );
+		assertTrue( Hibernate.isInitialized( dp ) );
+		checkReadOnly( s, dp, false );
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( dpOrig.getDescription(), dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testReadOnlyViaLazyInitializerNoChangeAfterInit() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		LazyInitializer dpLI = ( ( HibernateProxy ) dp ).getHibernateLazyInitializer();
+		checkReadOnly( s, dp, false );
+		assertTrue( dpLI.isUninitialized() );
+		Hibernate.initialize( dp );
+		assertFalse( dpLI.isUninitialized() );
+		checkReadOnly( s, dp, false );
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		dpLI = ( ( HibernateProxy ) dp ).getHibernateLazyInitializer();
+		dpLI.setReadOnly( true );
+		checkReadOnly( s, dp, true );
+		assertTrue( dpLI.isUninitialized() );
+		Hibernate.initialize( dp );
+		assertFalse( dpLI.isUninitialized() );
+		checkReadOnly( s, dp, true );
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		dpLI = ( ( HibernateProxy ) dp ).getHibernateLazyInitializer();
+		dpLI.setReadOnly( true );
+		checkReadOnly( s, dp, true );
+		assertTrue( dpLI.isUninitialized() );
+		dpLI.setReadOnly( false );
+		checkReadOnly( s, dp, false );
+		assertTrue( dpLI.isUninitialized() );
+		Hibernate.initialize( dp );
+		assertFalse( dpLI.isUninitialized() );
+		checkReadOnly( s, dp, false );
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( dpOrig.getDescription(), dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testReadOnlyViaSessionBeforeInit() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		s.setReadOnly( dp, true );
+		dp.setDescription( "changed" );
+		assertTrue( Hibernate.isInitialized( dp ) );
+		assertEquals( "changed", dp.getDescription() );
+		checkReadOnly( s, dp, true );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( dpOrig.getDescription(), dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testModifiableViaSessionBeforeInit() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		checkReadOnly( s, dp, false );
+		dp.setDescription( "changed" );
+		assertTrue( Hibernate.isInitialized( dp ) );
+		assertEquals( "changed", dp.getDescription() );
+		checkReadOnly( s, dp, false );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( "changed", dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testReadOnlyViaSessionBeforeInitByModifiableQuery() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		checkReadOnly( s, dp, false );
+		s.setReadOnly( dp, true );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		checkReadOnly( s, dp, true );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		DataPoint dpFromQuery = ( DataPoint ) s.createQuery( "from DataPoint where id=" + dpOrig.getId() ).setReadOnly( false ).uniqueResult();
+		assertTrue( Hibernate.isInitialized( dpFromQuery ) );
+		assertSame( dp, dpFromQuery );
+		checkReadOnly( s, dp, true );
+		dp.setDescription( "changed" );
+		assertEquals( "changed", dp.getDescription() );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( dpOrig.getDescription(), dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testReadOnlyViaSessionBeforeInitByReadOnlyQuery() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		checkReadOnly( s, dp, false );
+		s.setReadOnly( dp, true );
+		checkReadOnly( s, dp, true );
+		DataPoint dpFromQuery = ( DataPoint ) s.createQuery( "from DataPoint where id=" + dpOrig.getId() ).setReadOnly( true ).uniqueResult();
+		assertTrue( Hibernate.isInitialized( dpFromQuery ) );
+		assertSame( dp, dpFromQuery );
+		checkReadOnly( s, dp, true );
+		dp.setDescription( "changed" );
+		assertEquals( "changed", dp.getDescription() );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( dpOrig.getDescription(), dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testModifiableViaSessionBeforeInitByModifiableQuery() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		checkReadOnly( s, dp, false );
+		DataPoint dpFromQuery = ( DataPoint ) s.createQuery( "from DataPoint where id=" + dpOrig.getId() ).setReadOnly( false ).uniqueResult();
+		assertTrue( Hibernate.isInitialized( dpFromQuery ) );
+		assertSame( dp, dpFromQuery );
+		checkReadOnly( s, dp, false );
+		dp.setDescription( "changed" );
+		assertEquals( "changed", dp.getDescription() );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( "changed", dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testModifiableViaSessionBeforeInitByReadOnlyQuery() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		checkReadOnly( s, dp, false );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		DataPoint dpFromQuery = ( DataPoint ) s.createQuery( "from DataPoint where id=" + dpOrig.getId() ).setReadOnly( true ).uniqueResult();
+		assertTrue( Hibernate.isInitialized( dpFromQuery ) );
+		assertSame( dp, dpFromQuery );
+		checkReadOnly( s, dp, false );
+		dp.setDescription( "changed" );
+		assertEquals( "changed", dp.getDescription() );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( "changed", dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testReadOnlyViaLazyInitializerBeforeInit() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		LazyInitializer dpLI = ( ( HibernateProxy ) dp ).getHibernateLazyInitializer();
+		assertTrue( dpLI.isUninitialized() );
+		checkReadOnly( s, dp, false );
+		dpLI.setReadOnly( true );
+		checkReadOnly( s, dp, true );
+		dp.setDescription( "changed" );
+		assertFalse( dpLI.isUninitialized() );
+		assertEquals( "changed", dp.getDescription() );
+		checkReadOnly( s, dp, true );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( dpOrig.getDescription(), dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testModifiableViaLazyInitializerBeforeInit() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		LazyInitializer dpLI = ( ( HibernateProxy ) dp ).getHibernateLazyInitializer();
+		assertTrue( dp instanceof HibernateProxy );
+		assertTrue( dpLI.isUninitialized() );
+		checkReadOnly( s, dp, false );
+		dp.setDescription( "changed" );
+		assertFalse( dpLI.isUninitialized() );
+		assertEquals( "changed", dp.getDescription() );
+		checkReadOnly( s, dp, false );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( "changed", dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+
+	public void testReadOnlyViaLazyInitializerAfterInit() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		LazyInitializer dpLI = ( ( HibernateProxy ) dp ).getHibernateLazyInitializer();
+		assertTrue( dpLI.isUninitialized() );
+		checkReadOnly( s, dp, false );
+		dp.setDescription( "changed" );
+		assertFalse( dpLI.isUninitialized() );
+		assertEquals( "changed", dp.getDescription() );
+		checkReadOnly( s, dp, false );
+		dpLI.setReadOnly( true );
+		checkReadOnly( s, dp, true );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( dpOrig.getDescription(), dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testModifiableViaLazyInitializerAfterInit() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		LazyInitializer dpLI = ( ( HibernateProxy ) dp ).getHibernateLazyInitializer();
+		assertTrue( dpLI.isUninitialized() );
+		checkReadOnly( s, dp, false );
+		dp.setDescription( "changed" );
+		assertFalse( dpLI.isUninitialized() );
+		assertEquals( "changed", dp.getDescription() );
+		checkReadOnly( s, dp, false );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( "changed", dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testModifyToReadOnlyToModifiableIsUpdatedFailureExpected() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		assertFalse( Hibernate.isInitialized( dp ));
+		checkReadOnly( s, dp, false );
+		dp.setDescription( "changed" );
+		assertTrue( Hibernate.isInitialized( dp ) );
+		assertEquals( "changed", dp.getDescription() );
+		s.setReadOnly( dp, true );
+		checkReadOnly( s, dp,true );
+		s.setReadOnly( dp, false );
+		checkReadOnly( s, dp, false );
+		assertEquals( "changed", dp.getDescription() );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( dpOrig.getDescription(), dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		try {
+			assertEquals( "changed", dp.getDescription() );
+			// should fail due to HHH-4642
+		}
+		finally {
+			s.getTransaction().rollback();
+			s.close();
+			s = openSession();
+			s.beginTransaction();			
+			s.delete( dp );
+			s.getTransaction().commit();
+			s.close();
+		}
+	}
+
+	public void testReadOnlyModifiedToModifiableIsUpdatedFailureExpected() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		assertFalse( Hibernate.isInitialized( dp ));
+		checkReadOnly( s, dp, false );
+		s.setReadOnly( dp, true );
+		checkReadOnly( s, dp,true );
+		dp.setDescription( "changed" );
+		assertTrue( Hibernate.isInitialized( dp ) );
+		assertEquals( "changed", dp.getDescription() );
+		s.setReadOnly( dp, false );
+		checkReadOnly( s, dp, false );
+		assertEquals( "changed", dp.getDescription() );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( dpOrig.getDescription(), dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		try {
+			assertEquals( "changed", dp.getDescription() );
+			// should fail due to HHH-4642
+		}
+		finally {
+			s.getTransaction().rollback();
+			s.close();
+			s = openSession();
+			s.beginTransaction();
+			s.delete( dp );
+			s.getTransaction().commit();
+			s.close();
+		}
+	}
+
+	public void testReadOnlyChangedEvictedUpdate() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		assertFalse( Hibernate.isInitialized( dp ));
+		checkReadOnly( s, dp, false );
+		s.setReadOnly( dp, true );
+		checkReadOnly( s, dp,true );
+		dp.setDescription( "changed" );
+		assertTrue( Hibernate.isInitialized( dp ) );
+		assertEquals( "changed", dp.getDescription() );
+		s.evict( dp );
+		assertFalse( s.contains( dp ) );
+		s.update( dp );
+		checkReadOnly( s, dp, false );
+		assertEquals( "changed", dp.getDescription() );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( "changed", dp.getDescription() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}	
+
+	public void testReadOnlyToModifiableInitWhenModifiedIsUpdated() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		checkReadOnly( s, dp, false );
+		s.setReadOnly( dp, true );
+		checkReadOnly( s, dp,true );
+		s.setReadOnly( dp, false );
+		checkReadOnly( s, dp, false );
+		assertFalse( Hibernate.isInitialized( dp ));
+		dp.setDescription( "changed" );
+		assertTrue( Hibernate.isInitialized( dp ) );
+		assertEquals( "changed", dp.getDescription() );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( "changed", dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testReadOnlyInitToModifiableModifiedIsUpdated() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		checkReadOnly( s, dp, false );
+		s.setReadOnly( dp, true );
+		checkReadOnly( s, dp,true );
+		assertFalse( Hibernate.isInitialized( dp ));
+		Hibernate.initialize( dp );
+		assertTrue( Hibernate.isInitialized( dp ));
+		checkReadOnly( s, dp,true );
+		s.setReadOnly( dp, false );
+		checkReadOnly( s, dp, false );
+		dp.setDescription( "changed" );
+		assertTrue( Hibernate.isInitialized( dp ) );
+		assertEquals( "changed", dp.getDescription() );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( "changed", dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testReadOnlyModifiedUpdate() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		checkReadOnly( s, dp, false );
+		s.setReadOnly( dp, true );
+		checkReadOnly( s, dp,true );
+		assertFalse( Hibernate.isInitialized( dp ));
+		dp.setDescription( "changed" );
+		assertTrue( Hibernate.isInitialized( dp ) );
+		assertEquals( "changed", dp.getDescription() );
+		checkReadOnly( s, dp,true );
+		s.update( dp );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( dpOrig.getDescription(), dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testReadOnlyDelete() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		checkReadOnly( s, dp, false );
+		s.setReadOnly( dp, true );
+		checkReadOnly( s, dp,true );
+		assertFalse( Hibernate.isInitialized( dp ));
+		s.delete( dp );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertNull( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testReadOnlyProxyMergeDetachedProxyWithChange() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		checkReadOnly( s, dp, false );
+		assertFalse( Hibernate.isInitialized( dp ));
+		Hibernate.initialize( dp );
+		assertTrue( Hibernate.isInitialized( dp ) );
+		s.getTransaction().commit();
+		s.close();
+
+		// modify detached proxy
+		dp.setDescription( "changed" );
+
+		s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dpLoaded = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dpLoaded instanceof HibernateProxy );
+		checkReadOnly( s, dpLoaded, false );
+		s.setReadOnly( dpLoaded, true );
+		checkReadOnly( s, dpLoaded,true );
+		assertFalse( Hibernate.isInitialized( dpLoaded ) );
+		DataPoint dpMerged = ( DataPoint ) s.merge( dp );
+		assertSame( dpLoaded, dpMerged );
+		assertTrue( Hibernate.isInitialized( dpLoaded ) );
+		assertEquals( "changed", dpLoaded.getDescription() );
+		checkReadOnly( s, dpLoaded, true );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( dpOrig.getDescription(), dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testReadOnlyProxyInitMergeDetachedProxyWithChange() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		checkReadOnly( s, dp, false );
+		assertFalse( Hibernate.isInitialized( dp ));
+		Hibernate.initialize( dp );
+		assertTrue( Hibernate.isInitialized( dp ) );
+		s.getTransaction().commit();
+		s.close();
+
+		// modify detached proxy
+		dp.setDescription( "changed" );
+
+		s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dpLoaded = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dpLoaded instanceof HibernateProxy );
+		assertFalse( Hibernate.isInitialized( dpLoaded ) );
+		Hibernate.initialize( dpLoaded );
+		assertTrue( Hibernate.isInitialized( dpLoaded ) );
+		checkReadOnly( s, dpLoaded, false );
+		s.setReadOnly( dpLoaded, true );
+		checkReadOnly( s, dpLoaded,true );
+		DataPoint dpMerged = ( DataPoint ) s.merge( dp );
+		assertSame( dpLoaded, dpMerged );
+		assertEquals( "changed", dpLoaded.getDescription() );
+		checkReadOnly( s, dpLoaded, true );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( dpOrig.getDescription(), dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testReadOnlyProxyMergeDetachedEntityWithChange() {
+
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		checkReadOnly( s, dp, false );
+		assertFalse( Hibernate.isInitialized( dp ));
+		Hibernate.initialize( dp );
+		assertTrue( Hibernate.isInitialized( dp ) );
+		s.getTransaction().commit();
+		s.close();
+
+		// modify detached proxy target
+		DataPoint dpEntity = ( DataPoint ) ( ( HibernateProxy ) dp ).getHibernateLazyInitializer().getImplementation();
+		dpEntity.setDescription( "changed" );
+
+		s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dpLoaded = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dpLoaded instanceof HibernateProxy );
+		checkReadOnly( s, dpLoaded, false );
+		s.setReadOnly( dpLoaded, true );
+		checkReadOnly( s, dpLoaded,true );
+		assertFalse( Hibernate.isInitialized( dpLoaded ) );
+		DataPoint dpMerged = ( DataPoint ) s.merge( dpEntity );
+		assertSame( dpLoaded, dpMerged );
+		assertTrue( Hibernate.isInitialized( dpLoaded ) );
+		assertEquals( "changed", dpLoaded.getDescription() );
+		checkReadOnly( s, dpLoaded, true );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( dpOrig.getDescription(), dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testReadOnlyProxyInitMergeDetachedEntityWithChange() {
+
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		checkReadOnly( s, dp, false );
+		assertFalse( Hibernate.isInitialized( dp ));
+		Hibernate.initialize( dp );
+		assertTrue( Hibernate.isInitialized( dp ) );
+		s.getTransaction().commit();
+		s.close();
+
+		// modify detached proxy target
+		DataPoint dpEntity = ( DataPoint ) ( ( HibernateProxy ) dp ).getHibernateLazyInitializer().getImplementation();
+		dpEntity.setDescription( "changed" );
+
+		s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dpLoaded = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dpLoaded instanceof HibernateProxy );
+		assertFalse( Hibernate.isInitialized( dpLoaded ) );
+		Hibernate.initialize( dpLoaded );
+		assertTrue( Hibernate.isInitialized( dpLoaded ) );
+		checkReadOnly( s, dpLoaded, false );
+		s.setReadOnly( dpLoaded, true );
+		checkReadOnly( s, dpLoaded,true );
+		DataPoint dpMerged = ( DataPoint ) s.merge( dpEntity );
+		assertSame( dpLoaded, dpMerged );
+		assertEquals( "changed", dpLoaded.getDescription() );
+		checkReadOnly( s, dpLoaded, true );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( dpOrig.getDescription(), dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testReadOnlyEntityMergeDetachedProxyWithChange() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		checkReadOnly( s, dp, false );
+		assertFalse( Hibernate.isInitialized( dp ));
+		Hibernate.initialize( dp );
+		assertTrue( Hibernate.isInitialized( dp ) );
+		s.getTransaction().commit();
+		s.close();
+
+		// modify detached proxy
+		dp.setDescription( "changed" );
+
+		s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
+		DataPoint dpEntity = ( DataPoint ) s.get( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertFalse( dpEntity instanceof HibernateProxy );
+		assertFalse( s.isReadOnly( dpEntity ) );
+		s.setReadOnly( dpEntity, true );
+		assertTrue( s.isReadOnly( dpEntity ) );
+		DataPoint dpMerged = ( DataPoint ) s.merge( dp );
+		assertSame( dpEntity, dpMerged );
+		assertEquals( "changed", dpEntity.getDescription() );
+		assertTrue( s.isReadOnly( dpEntity ) );
+		s.flush();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( dpOrig.getDescription(), dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testSetReadOnlyInTwoTransactionsSameSession() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		checkReadOnly( s, dp, false );
+		s.setReadOnly( dp, true );
+		checkReadOnly( s, dp, true );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		dp.setDescription( "changed" );
+		assertTrue( Hibernate.isInitialized( dp ) );
+		assertEquals( "changed", dp.getDescription() );
+		s.flush();
+		s.getTransaction().commit();
+
+		checkReadOnly( s, dp, true );
+
+		s.beginTransaction();
+		checkReadOnly( s, dp, true );
+		dp.setDescription( "changed again" );
+		assertEquals( "changed again", dp.getDescription() );
+		s.flush();
+		s.getTransaction().commit();
+
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( dpOrig.getDescription(), dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testSetReadOnlyBetweenTwoTransactionsSameSession() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		checkReadOnly( s, dp, false );
+		dp.setDescription( "changed" );
+		assertTrue( Hibernate.isInitialized( dp ) );
+		assertEquals( "changed", dp.getDescription() );
+		checkReadOnly( s, dp, false );
+		s.flush();
+		s.getTransaction().commit();
+
+		checkReadOnly( s, dp, false );
+		s.setReadOnly( dp, true );
+		checkReadOnly( s, dp, true );
+
+		s.beginTransaction();
+		checkReadOnly( s, dp, true );
+		dp.setDescription( "changed again" );
+		assertEquals( "changed again", dp.getDescription() );
+		s.flush();
+		s.getTransaction().commit();
+
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( "changed", dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testSetModifiableBetweenTwoTransactionsSameSession() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		checkReadOnly( s, dp, false );
+		s.setReadOnly( dp, true );
+		checkReadOnly( s, dp, true );
+		dp.setDescription( "changed" );
+		assertTrue( Hibernate.isInitialized( dp ) );
+		assertEquals( "changed", dp.getDescription() );
+		checkReadOnly( s, dp, true );
+		s.flush();
+		s.getTransaction().commit();
+
+		checkReadOnly( s, dp, true );
+		s.setReadOnly( dp, false );
+		checkReadOnly( s, dp, false );
+
+		s.beginTransaction();
+		checkReadOnly( s, dp, false );
+		assertEquals( "changed", dp.getDescription() );
+		s.refresh( dp );
+		assertEquals( dpOrig.getDescription(), dp.getDescription() );
+		checkReadOnly( s, dp, false );
+		dp.setDescription( "changed again" );
+		assertEquals( "changed again", dp.getDescription() );
+		s.flush();
+		s.getTransaction().commit();
+
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dpOrig.getId() );
+		assertEquals( dpOrig.getId(), dp.getId() );
+		assertEquals( "changed again", dp.getDescription() );
+		assertEquals( dpOrig.getX(), dp.getX() );
+		assertEquals( dpOrig.getY(), dp.getY() );
+		s.delete( dp );
+		s.getTransaction().commit();
+		s.close();
+	}
+
+	public void testIsReadOnlyAfterSessionClosed() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		checkReadOnly( s, dp, false );
+		s.getTransaction().commit();
+		s.close();
+
+		try {
+ 			s.isReadOnly( dp );
+			fail( "should have failed because session was closed" );
+		}
+		catch ( SessionException ex) {
+			// expected
+		}
+		finally {
+			s = openSession();
+			s.beginTransaction();
+			s.delete( dp );
+			s.getTransaction().commit();
+			s.close();
+		}
+	}
+
+	public void testIsReadOnlyAfterSessionClosedViaLazyInitializer() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		checkReadOnly( s, dp, false );
+		s.getTransaction().commit();
+		assertTrue( s.contains( dp ) );
+		s.close();
+
+		assertNull( ( ( HibernateProxy ) dp ).getHibernateLazyInitializer().getSession() );
+		try {
+ 			( ( HibernateProxy ) dp ).getHibernateLazyInitializer().isReadOnly();
+			fail( "should have failed because session was detached" );
+		}
+		catch ( TransientObjectException ex) {
+			// expected
+		}
+		finally {
+			s = openSession();
+			s.beginTransaction();
+			s.delete( dp );
+			s.getTransaction().commit();
+			s.close();
+		}
+	}
+
+
+	public void testDetachedIsReadOnlyAfterEvictViaSession() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		checkReadOnly( s, dp, false );
+		assertTrue( s.contains( dp ) );
+		s.evict( dp );
+		assertFalse( s.contains( dp ) );
+		assertNull( ( ( HibernateProxy ) dp ).getHibernateLazyInitializer().getSession() );
+
+		try {
+ 			s.isReadOnly( dp );
+			fail( "should have failed because proxy was detached" );
+		}
+		catch ( TransientObjectException ex) {
+			// expected
+		}
+		finally {
+			s.delete( dp );
+			s.getTransaction().commit();
+			s.close();
+		}
+	}
+
+	public void testDetachedIsReadOnlyAfterEvictViaLazyInitializer() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		checkReadOnly( s, dp, false );
+		s.evict( dp );
+		assertFalse( s.contains( dp ) );
+		assertNull( ( ( HibernateProxy ) dp ).getHibernateLazyInitializer().getSession() );
+		try {
+ 			( ( HibernateProxy ) dp ).getHibernateLazyInitializer().isReadOnly();
+			fail( "should have failed because proxy was detached" );
+		}
+		catch ( TransientObjectException ex) {
+			// expected
+		}
+		finally {
+			s.beginTransaction();
+			s.delete( dp );
+			s.getTransaction().commit();
+			s.close();
+		}
+	}
+
+	public void testSetReadOnlyAfterSessionClosed() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		checkReadOnly( s, dp, false );
+		s.getTransaction().commit();
+		s.close();
+
+		try {
+ 			s.setReadOnly( dp, true );
+			fail( "should have failed because session was closed" );
+		}
+		catch ( SessionException ex) {
+			// expected
+		}
+		finally {
+			s = openSession();
+			s.beginTransaction();
+			s.delete( dp );
+			s.getTransaction().commit();
+			s.close();
+		}
+	}
+
+	public void testSetReadOnlyAfterSessionClosedViaLazyInitializer() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		checkReadOnly( s, dp, false );
+		s.getTransaction().commit();
+		assertTrue( s.contains( dp ) );
+		s.close();
+
+		assertNull( ( ( HibernateProxy ) dp ).getHibernateLazyInitializer().getSession() );
+		try {
+ 			( ( HibernateProxy ) dp ).getHibernateLazyInitializer().setReadOnly( true );
+			fail( "should have failed because session was detached" );
+		}
+		catch ( TransientObjectException ex) {
+			// expected
+		}
+		finally {
+			s = openSession();
+			s.beginTransaction();
+			s.delete( dp );
+			s.getTransaction().commit();
+			s.close();
+		}
+	}
+
+
+	public void testDetachedSetReadOnlyAfterEvictViaSession() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		checkReadOnly( s, dp, false );
+		assertTrue( s.contains( dp ) );
+		s.evict( dp );
+		assertFalse( s.contains( dp ) );
+		assertNull( ( ( HibernateProxy ) dp ).getHibernateLazyInitializer().getSession() );
+
+		try {
+ 			s.setReadOnly( dp, true );
+			fail( "should have failed because proxy was detached" );
+		}
+		catch ( TransientObjectException ex) {
+			// expected
+		}
+		finally {
+			s.delete( dp );
+			s.getTransaction().commit();
+			s.close();
+		}
+	}
+
+	public void testDetachedSetReadOnlyAfterEvictViaLazyInitializer() {
+		DataPoint dpOrig = createDataPoint( CacheMode.IGNORE );
+
+		Session s = openSession();
+		s.setCacheMode(CacheMode.IGNORE);
+
+		s.beginTransaction();
+		DataPoint dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpOrig.getId() ) );
+		assertTrue( dp instanceof HibernateProxy );
+		assertFalse( Hibernate.isInitialized( dp ) );
+		checkReadOnly( s, dp, false );
+		s.evict( dp );
+		assertFalse( s.contains( dp ) );
+		assertNull( ( ( HibernateProxy ) dp ).getHibernateLazyInitializer().getSession() );
+		try {
+ 			( ( HibernateProxy ) dp ).getHibernateLazyInitializer().setReadOnly( true );
+			fail( "should have failed because proxy was detached" );
+		}
+		catch ( TransientObjectException ex) {
+			// expected
+		}
+		finally {
+			s.beginTransaction();
+			s.delete( dp );
+			s.getTransaction().commit();
+			s.close();
+		}
+	}
+
+	private DataPoint createDataPoint(CacheMode cacheMode) {
+		Session s = openSession();
+		s.setCacheMode( cacheMode );
+		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 );
+		s.getTransaction().commit();
+		s.close();
+		return dp;
+	}
+
+	private void checkReadOnly(Session s, Object proxy, boolean expectedReadOnly) {
+		assertTrue( proxy instanceof HibernateProxy );
+		LazyInitializer li = ( ( HibernateProxy ) proxy ).getHibernateLazyInitializer();
+		assertSame( s, li.getSession() );
+		assertEquals( expectedReadOnly, s.isReadOnly( proxy ) );
+		assertEquals( expectedReadOnly, li.isReadOnly() );
+		assertEquals( Hibernate.isInitialized( proxy ), ! li.isUninitialized() );
+		if ( Hibernate.isInitialized( proxy ) ) {
+			assertEquals( expectedReadOnly, s.isReadOnly( li.getImplementation() ) );
+		}
+	}
+}

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-12 21:00:11 UTC (rev 18524)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/readonly/ReadOnlyTest.java	2010-01-13 01:04:55 UTC (rev 18525)
@@ -67,7 +67,7 @@
 		return new FunctionalTestClassTestSuite( ReadOnlyTest.class );
 	}
 
-	public void testReadOnlyOnProxiesFailureExpected() {
+	public void testReadOnlyOnProxies() {
 		Session s = openSession();
 		s.setCacheMode( CacheMode.IGNORE );
 		s.beginTransaction();
@@ -82,6 +82,7 @@
 
 		s = openSession();
 		s.setCacheMode(CacheMode.IGNORE);
+		s.beginTransaction();
 		dp = ( DataPoint ) s.load( DataPoint.class, new Long( dpId ) );
 		assertFalse( "was initialized", Hibernate.isInitialized( dp ) );
 		s.setReadOnly( dp, true );
@@ -178,6 +179,138 @@
 
 	}
 
+	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.setCacheMode(CacheMode.IGNORE);
+		t = s.beginTransaction();
+		dp = ( DataPoint ) s.get( DataPoint.class, dp.getId() );
+		s.setReadOnly( dp, true );
+		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();
+		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 ) );
+		int i = 0;
+		ScrollableResults sr = s.createQuery("from DataPoint dp order by dp.x asc")
+				.setReadOnly(true)
+				.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
+				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( list.size(), nExpectedChanges );
+		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();
+		DataPoint dpLast = ( DataPoint ) s.get( DataPoint.class,  dp.getId() );
+		assertFalse( s.isReadOnly( dpLast ) );
+		s.setReadOnly( dpLast, true );
+		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( list.size(), nExpectedChanges );
+		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";



More information about the hibernate-commits mailing list