HHH-2501 HHH-4804 : Add default read-only/modifiable setting for session; fix setting for non-lazy associations loaded from Query

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18641 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Gail Badner 2010-01-27 06:36:55 +00:00
parent 3b536fa6f6
commit a0d8e6a415
25 changed files with 3055 additions and 40 deletions

View File

@ -175,11 +175,46 @@ public interface Query {
public Query setFirstResult(int firstResult); public Query setFirstResult(int firstResult);
/** /**
* Entities retrieved by this query will be loaded in * Should entities and proxies loaded by this Query be put in read-only mode? If the
* a read-only mode where Hibernate will never dirty-check * read-only/modifiable setting was not initialized, then the default
* them or make changes persistent. * 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); public Query setReadOnly(boolean readOnly);
/** /**

View File

@ -231,6 +231,44 @@ public interface Session extends Serializable {
*/ */
public boolean isDirty() throws HibernateException; 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 * Return the identifier value of the given entity as associated with this
* session. An exception is thrown if the given entity instance is transient * session. An exception is thrown if the given entity instance is transient
@ -842,6 +880,11 @@ public interface Session extends Serializable {
/** /**
* Is the specified entity or proxy read-only? * 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 * @param entityOrProxy, an entity or HibernateProxy
* @return true, the entity or proxy is read-only; * @return true, the entity or proxy is read-only;
* false, the entity or proxy is modifiable. * false, the entity or proxy is modifiable.
@ -850,16 +893,23 @@ public interface Session extends Serializable {
/** /**
* Set an unmodified persistent object to read-only mode, or a read-only * 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 * object to modifiable mode. In read-only mode, no snapshot is maintained,
* and the instance is never dirty checked. * the instance is never dirty checked, and changes are not persisted.
* *
* If the entity or proxy already has the specified read-only/modifiable * If the entity or proxy already has the specified read-only/modifiable
* setting, then this method does nothing. * 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 entityOrProxy, an entity or HibernateProxy
* @param readOnly, if true, the entity or proxy is made read-only; * @param readOnly, if true, the entity or proxy is made read-only;
* if false, the entity or proxy is made modifiable. * if false, the entity or proxy is made modifiable.
* @see Query#setReadOnly(boolean)
*/ */
public void setReadOnly(Object entityOrProxy, boolean readOnly); public void setReadOnly(Object entityOrProxy, boolean readOnly);

View File

@ -476,23 +476,92 @@ public interface PersistenceContext {
*/ */
public boolean isPropertyNull(EntityKey ownerKey, String propertyName); 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? * 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 * @param entityOrProxy
* @return true, the object is read-only; false, the object is modifiable. * @return true, the object is read-only; false, the object is modifiable.
*/ */
public boolean isReadOnly(Object entityOrProxy); 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 * If the entity or proxy already has the specified read-only/modifiable
* setting, then this method does nothing. * 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 entityOrProxy, an entity or HibernateProxy
* @param readOnly, if true, the entity or proxy is made read-only; * @param readOnly, if true, the entity or proxy is made read-only;
* if false, the entity or proxy is made modifiable. * if false, the entity or proxy is made modifiable.
*
* @see org.hibernate.Session#setReadOnly(Object, boolean)
*/ */
public void setReadOnly(Object entityOrProxy, boolean readOnly); public void setReadOnly(Object entityOrProxy, boolean readOnly);

View File

@ -66,6 +66,7 @@ public final class QueryParameters {
private Object optionalObject; private Object optionalObject;
private String optionalEntityName; private String optionalEntityName;
private Serializable optionalId; private Serializable optionalId;
private boolean isReadOnlyInitialized;
private boolean readOnly; private boolean readOnly;
private boolean callable = false; private boolean callable = false;
private boolean autodiscovertypes = false; private boolean autodiscovertypes = false;
@ -124,6 +125,7 @@ public final class QueryParameters {
null, null,
false, false,
false, false,
false,
null, null,
null, null,
collectionKeys, collectionKeys,
@ -149,6 +151,7 @@ public final class QueryParameters {
lockOptions, lockOptions,
rowSelection, rowSelection,
false, false,
false,
cacheable, cacheable,
cacheRegion, cacheRegion,
comment, comment,
@ -164,6 +167,7 @@ public final class QueryParameters {
final Map namedParameters, final Map namedParameters,
final LockOptions lockOptions, final LockOptions lockOptions,
final RowSelection rowSelection, final RowSelection rowSelection,
final boolean isReadOnlyInitialized,
final boolean readOnly, final boolean readOnly,
final boolean cacheable, final boolean cacheable,
final String cacheRegion, final String cacheRegion,
@ -181,6 +185,7 @@ public final class QueryParameters {
//this.forceCacheRefresh = forceCacheRefresh; //this.forceCacheRefresh = forceCacheRefresh;
this.comment = comment; this.comment = comment;
this.collectionKeys = collectionKeys; this.collectionKeys = collectionKeys;
this.isReadOnlyInitialized = isReadOnlyInitialized;
this.readOnly = readOnly; this.readOnly = readOnly;
this.resultTransformer = transformer; this.resultTransformer = transformer;
} }
@ -191,6 +196,7 @@ public final class QueryParameters {
final Map namedParameters, final Map namedParameters,
final LockOptions lockOptions, final LockOptions lockOptions,
final RowSelection rowSelection, final RowSelection rowSelection,
final boolean isReadOnlyInitialized,
final boolean readOnly, final boolean readOnly,
final boolean cacheable, final boolean cacheable,
final String cacheRegion, final String cacheRegion,
@ -207,6 +213,7 @@ public final class QueryParameters {
namedParameters, namedParameters,
lockOptions, lockOptions,
rowSelection, rowSelection,
isReadOnlyInitialized,
readOnly, readOnly,
cacheable, cacheable,
cacheRegion, cacheRegion,
@ -351,12 +358,82 @@ public final class QueryParameters {
this.optionalObject = optionalObject; 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() { public boolean isReadOnly() {
if ( ! isReadOnlyInitialized() ) {
throw new IllegalStateException( "cannot call isReadOnly() when isReadOnlyInitialized() returns false" );
}
return readOnly; 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) { public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly; this.readOnly = readOnly;
this.isReadOnlyInitialized = true;
} }
public void setCallable(boolean callable) { public void setCallable(boolean callable) {
@ -467,6 +544,7 @@ public final class QueryParameters {
this.namedParameters, this.namedParameters,
this.lockOptions, this.lockOptions,
selection, selection,
this.isReadOnlyInitialized,
this.readOnly, this.readOnly,
this.cacheable, this.cacheable,
this.cacheRegion, this.cacheRegion,

View File

@ -127,6 +127,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
private int loadCounter = 0; private int loadCounter = 0;
private boolean flushing = false; private boolean flushing = false;
private boolean defaultReadOnly = false;
private boolean hasNonReadOnlyEntities = false; private boolean hasNonReadOnlyEntities = false;
private LoadContexts loadContexts; private LoadContexts loadContexts;
@ -231,12 +232,27 @@ public class StatefulPersistenceContext implements PersistenceContext {
if ( batchFetchQueue != null ) { if ( batchFetchQueue != null ) {
batchFetchQueue.clear(); batchFetchQueue.clear();
} }
// defaultReadOnly is unaffected by clear()
hasNonReadOnlyEntities = false; hasNonReadOnlyEntities = false;
if ( loadContexts != null ) { if ( loadContexts != null ) {
loadContexts.cleanup(); loadContexts.cleanup();
} }
} }
/**
* {@inheritDoc}
*/
public boolean isDefaultReadOnly() {
return defaultReadOnly;
}
/**
* {@inheritDoc}
*/
public void setDefaultReadOnly(boolean defaultReadOnly) {
this.defaultReadOnly = defaultReadOnly;
}
public boolean hasNonReadOnlyEntities() { public boolean hasNonReadOnlyEntities() {
return hasNonReadOnlyEntities; return hasNonReadOnlyEntities;
} }
@ -1396,6 +1412,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
public void serialize(ObjectOutputStream oos) throws IOException { public void serialize(ObjectOutputStream oos) throws IOException {
log.trace( "serializing persistent-context" ); log.trace( "serializing persistent-context" );
oos.writeBoolean( defaultReadOnly );
oos.writeBoolean( hasNonReadOnlyEntities ); oos.writeBoolean( hasNonReadOnlyEntities );
oos.writeInt( entitiesByKey.size() ); oos.writeInt( entitiesByKey.size() );
@ -1491,6 +1508,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
// because serialization is used for different things. // because serialization is used for different things.
try { try {
rtn.defaultReadOnly = ois.readBoolean();
// todo : we can actually just determine this from the incoming EntityEntry-s // todo : we can actually just determine this from the incoming EntityEntry-s
rtn.hasNonReadOnlyEntities = ois.readBoolean(); rtn.hasNonReadOnlyEntities = ois.readBoolean();

View File

@ -298,6 +298,9 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
Object proxy = persister.createProxy( event.getEntityId(), event.getSession() ); Object proxy = persister.createProxy( event.getEntityId(), event.getSession() );
persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey(keyToLoad); persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey(keyToLoad);
persistenceContext.addProxy(keyToLoad, proxy); persistenceContext.addProxy(keyToLoad, proxy);
( ( HibernateProxy ) proxy )
.getHibernateLazyInitializer()
.setReadOnly( event.getSession().isDefaultReadOnly() || ! persister.isMutable() );
return proxy; return proxy;
} }
} }
@ -599,7 +602,10 @@ public class DefaultLoadEventListener extends AbstractLockUpgradeEventListener i
final PersistenceContext persistenceContext = session.getPersistenceContext(); final PersistenceContext persistenceContext = session.getPersistenceContext();
persistenceContext.addEntry( persistenceContext.addEntry(
result, result,
Status.MANAGED, ( session.isDefaultReadOnly() || ! persister.isMutable() ?
Status.READ_ONLY :
Status.MANAGED
),
values, values,
null, null,
id, id,

View File

@ -101,7 +101,7 @@ public abstract class AbstractQueryImpl implements Query {
private FlushMode sessionFlushMode; private FlushMode sessionFlushMode;
private CacheMode sessionCacheMode; private CacheMode sessionCacheMode;
private Serializable collectionKey; private Serializable collectionKey;
private boolean readOnly; private Boolean readOnly;
private ResultTransformer resultTransformer; private ResultTransformer resultTransformer;
public AbstractQueryImpl( public AbstractQueryImpl(
@ -202,12 +202,21 @@ public abstract class AbstractQueryImpl implements Query {
return this; return this;
} }
/**
* {@inheritDoc}
*/
public boolean isReadOnly() { public boolean isReadOnly() {
return readOnly; return ( readOnly == null ?
getSession().getPersistenceContext().isDefaultReadOnly() :
readOnly.booleanValue()
);
} }
/**
* {@inheritDoc}
*/
public Query setReadOnly(boolean readOnly) { public Query setReadOnly(boolean readOnly) {
this.readOnly = readOnly; this.readOnly = Boolean.valueOf( readOnly );
return this; return this;
} }
@ -881,7 +890,8 @@ public abstract class AbstractQueryImpl implements Query {
namedParams, namedParams,
getLockOptions(), getLockOptions(),
getSelection(), getSelection(),
readOnly, true,
isReadOnly(),
cacheable, cacheable,
cacheRegion, cacheRegion,
comment, comment,

View File

@ -1956,6 +1956,20 @@ public final class SessionImpl extends AbstractSessionImpl
return true; return true;
} }
/**
* {@inheritDoc}
*/
public boolean isDefaultReadOnly() {
return persistenceContext.isDefaultReadOnly();
}
/**
* {@inheritDoc}
*/
public void setDefaultReadOnly(boolean defaultReadOnly) {
persistenceContext.setDefaultReadOnly( defaultReadOnly );
}
public boolean isReadOnly(Object entityOrProxy) { public boolean isReadOnly(Object entityOrProxy) {
errorIfClosed(); errorIfClosed();
checkTransactionSynchStatus(); checkTransactionSynchStatus();

View File

@ -483,6 +483,22 @@ public class StatelessSessionImpl extends AbstractSessionImpl
return false; 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! //TODO: COPY/PASTE FROM SessionImpl, pull up!

View File

@ -253,15 +253,32 @@ public abstract class Loader {
throws HibernateException, SQLException { throws HibernateException, SQLException {
final PersistenceContext persistenceContext = session.getPersistenceContext(); 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(); persistenceContext.beforeLoad();
List result; List result;
try { try {
result = doQuery( session, queryParameters, returnProxies ); try {
result = doQuery( session, queryParameters, returnProxies );
}
finally {
persistenceContext.afterLoad();
}
persistenceContext.initializeNonLazyCollections();
} }
finally { finally {
persistenceContext.afterLoad(); // Restore the original default
persistenceContext.setDefaultReadOnly( defaultReadOnlyOrig );
} }
persistenceContext.initializeNonLazyCollections();
return result; return result;
} }
@ -312,7 +329,7 @@ public abstract class Loader {
hydratedObjects, hydratedObjects,
resultSet, resultSet,
session, session,
queryParameters.isReadOnly() queryParameters.isReadOnly( session )
); );
session.getPersistenceContext().initializeNonLazyCollections(); session.getPersistenceContext().initializeNonLazyCollections();
return result; return result;
@ -363,7 +380,7 @@ public abstract class Loader {
hydratedObjects, hydratedObjects,
resultSet, resultSet,
session, session,
queryParameters.isReadOnly() queryParameters.isReadOnly( session )
); );
session.getPersistenceContext().initializeNonLazyCollections(); session.getPersistenceContext().initializeNonLazyCollections();
return result; return result;
@ -749,7 +766,7 @@ public abstract class Loader {
session.getBatcher().closeQueryStatement( st, rs ); session.getBatcher().closeQueryStatement( st, rs );
} }
initializeEntitiesAndCollections( hydratedObjects, rs, session, queryParameters.isReadOnly() ); initializeEntitiesAndCollections( hydratedObjects, rs, session, queryParameters.isReadOnly( session ) );
if ( createSubselects ) createSubselects( subselectResultKeys, queryParameters, session ); if ( createSubselects ) createSubselects( subselectResultKeys, queryParameters, session );
@ -1884,7 +1901,7 @@ public abstract class Loader {
try { try {
result = doQueryAndInitializeNonLazyCollections( result = doQueryAndInitializeNonLazyCollections(
session, session,
new QueryParameters( new QueryParameters(
new Type[] { identifierType }, new Type[] { identifierType },
new Object[] { id }, new Object[] { id },
optionalObject, optionalObject,

View File

@ -65,11 +65,11 @@ public abstract class AbstractLazyInitializer implements LazyInitializer {
protected AbstractLazyInitializer(String entityName, Serializable id, SessionImplementor session) { protected AbstractLazyInitializer(String entityName, Serializable id, SessionImplementor session) {
this.entityName = entityName; this.entityName = entityName;
this.id = id; this.id = id;
this.readOnly = false;
// initialize other fields depending on session state // initialize other fields depending on session state
if ( session == null ) { if ( session == null ) {
// would be better to call unsetSession(), but it is not final... // would be better to call unsetSession(), but it is not final...
session = null; session = null;
readOnly = false;
} }
else { else {
setSession( session ); setSession( session );
@ -116,11 +116,11 @@ public abstract class AbstractLazyInitializer implements LazyInitializer {
*/ */
public final void setSession(SessionImplementor s) throws HibernateException { public final void setSession(SessionImplementor s) throws HibernateException {
if ( s != session ) { if ( s != session ) {
readOnly = false;
// check for s == null first, since it is least expensive // check for s == null first, since it is least expensive
if ( s == null ){ if ( s == null ){
// would be better to call unsetSession(), but it is not final... // would be better to call unsetSession(), but it is not final...
session = null; session = null;
readOnly = false;
} }
else if ( isConnectedToSession() ) { else if ( isConnectedToSession() ) {
//TODO: perhaps this should be some other RuntimeException... //TODO: perhaps this should be some other RuntimeException...
@ -128,8 +128,6 @@ public abstract class AbstractLazyInitializer implements LazyInitializer {
} }
else { else {
session = s; session = s;
// NOTE: the proxy may not be connected to the session yet, so set readOnly directly
readOnly = ! session.getFactory().getEntityPersister( entityName ).isMutable();
} }
} }
} }

View File

@ -212,6 +212,7 @@ public final class CGLIBLazyInitializer extends BasicLazyInitializer implements
persistentClass, persistentClass,
interfaces, interfaces,
getIdentifier(), getIdentifier(),
( getSession() != null && getSession().isOpen() ? isReadOnly() : false ),
getIdentifierMethod, getIdentifierMethod,
setIdentifierMethod, setIdentifierMethod,
componentIdType componentIdType

View File

@ -28,6 +28,7 @@ import java.io.Serializable;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.type.AbstractComponentType; import org.hibernate.type.AbstractComponentType;
/** /**
@ -39,6 +40,7 @@ public final class SerializableProxy implements Serializable {
private Class persistentClass; private Class persistentClass;
private Class[] interfaces; private Class[] interfaces;
private Serializable id; private Serializable id;
private boolean readOnly;
private Class getIdentifierMethodClass; private Class getIdentifierMethodClass;
private Class setIdentifierMethodClass; private Class setIdentifierMethodClass;
private String getIdentifierMethodName; private String getIdentifierMethodName;
@ -53,6 +55,7 @@ public final class SerializableProxy implements Serializable {
final Class persistentClass, final Class persistentClass,
final Class[] interfaces, final Class[] interfaces,
final Serializable id, final Serializable id,
final boolean readOnly,
final Method getIdentifierMethod, final Method getIdentifierMethod,
final Method setIdentifierMethod, final Method setIdentifierMethod,
AbstractComponentType componentIdType AbstractComponentType componentIdType
@ -61,6 +64,7 @@ public final class SerializableProxy implements Serializable {
this.persistentClass = persistentClass; this.persistentClass = persistentClass;
this.interfaces = interfaces; this.interfaces = interfaces;
this.id = id; this.id = id;
this.readOnly = readOnly;
if (getIdentifierMethod!=null) { if (getIdentifierMethod!=null) {
getIdentifierMethodClass = getIdentifierMethod.getDeclaringClass(); getIdentifierMethodClass = getIdentifierMethod.getDeclaringClass();
getIdentifierMethodName = getIdentifierMethod.getName(); getIdentifierMethodName = getIdentifierMethod.getName();

View File

@ -229,6 +229,7 @@ public class JavassistLazyInitializer extends BasicLazyInitializer implements Me
persistentClass, persistentClass,
interfaces, interfaces,
getIdentifier(), getIdentifier(),
( getSession() != null && getSession().isOpen() ? isReadOnly() : false ),
getIdentifierMethod, getIdentifierMethod,
setIdentifierMethod, setIdentifierMethod,
componentIdType componentIdType

View File

@ -28,6 +28,7 @@ import java.io.Serializable;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.type.AbstractComponentType; import org.hibernate.type.AbstractComponentType;
/** /**
@ -39,6 +40,7 @@ public final class SerializableProxy implements Serializable {
private Class persistentClass; private Class persistentClass;
private Class[] interfaces; private Class[] interfaces;
private Serializable id; private Serializable id;
private boolean readOnly;
private Class getIdentifierMethodClass; private Class getIdentifierMethodClass;
private Class setIdentifierMethodClass; private Class setIdentifierMethodClass;
private String getIdentifierMethodName; private String getIdentifierMethodName;
@ -53,6 +55,7 @@ public final class SerializableProxy implements Serializable {
final Class persistentClass, final Class persistentClass,
final Class[] interfaces, final Class[] interfaces,
final Serializable id, final Serializable id,
final boolean readOnly,
final Method getIdentifierMethod, final Method getIdentifierMethod,
final Method setIdentifierMethod, final Method setIdentifierMethod,
AbstractComponentType componentIdType AbstractComponentType componentIdType
@ -61,6 +64,7 @@ public final class SerializableProxy implements Serializable {
this.persistentClass = persistentClass; this.persistentClass = persistentClass;
this.interfaces = interfaces; this.interfaces = interfaces;
this.id = id; this.id = id;
this.readOnly = readOnly;
if (getIdentifierMethod!=null) { if (getIdentifierMethod!=null) {
getIdentifierMethodClass = getIdentifierMethod.getDeclaringClass(); getIdentifierMethodClass = getIdentifierMethod.getDeclaringClass();
getIdentifierMethodName = getIdentifierMethod.getName(); getIdentifierMethodName = getIdentifierMethod.getName();

View File

@ -29,6 +29,7 @@ import java.util.Iterator;
import junit.framework.Test; import junit.framework.Test;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.Transaction; import org.hibernate.Transaction;
@ -53,7 +54,234 @@ public class ImmutableTest extends FunctionalTestCase {
return new FunctionalTestClassTestSuite( ImmutableTest.class ); 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() { public void testImmutable() {
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, 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 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"); Contract c = new Contract("gavin", "phone");
ContractVariation cv1 = new ContractVariation(1, c); ContractVariation cv1 = new ContractVariation(1, c);
cv1.setText("expensive"); cv1.setText("expensive");
@ -64,20 +292,12 @@ public class ImmutableTest extends FunctionalTestCase {
s.persist(c); s.persist(c);
t.commit(); t.commit();
s.close(); s.close();
s = openSession();
t = s.beginTransaction();
c = (Contract) s.createCriteria(Contract.class).uniqueResult();
c.setCustomerName("foo bar");
c.getVariations().add( new ContractVariation(3, c) );
cv1 = (ContractVariation) c.getVariations().iterator().next();
cv1.setText("blah blah");
t.commit();
s.close();
s = openSession(); s = openSession();
t = s.beginTransaction(); t = s.beginTransaction();
c = (Contract) s.createCriteria(Contract.class).uniqueResult(); 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.getCustomerName(), "gavin" );
assertEquals( c.getVariations().size(), 2 ); assertEquals( c.getVariations().size(), 2 );
Iterator it = c.getVariations().iterator(); Iterator it = c.getVariations().iterator();
@ -85,6 +305,10 @@ public class ImmutableTest extends FunctionalTestCase {
assertEquals( cv1.getText(), "expensive" ); assertEquals( cv1.getText(), "expensive" );
cv2 = (ContractVariation) it.next(); cv2 = (ContractVariation) it.next();
assertEquals( cv2.getText(), "more expensive" ); 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); s.delete(c);
assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); assertEquals( s.createCriteria(Contract.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) );
assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) ); assertEquals( s.createCriteria(ContractVariation.class).setProjection( Projections.rowCount() ).uniqueResult(), new Long(0) );
@ -92,6 +316,117 @@ public class ImmutableTest extends FunctionalTestCase {
s.close(); 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() { public void testImmutableParentEntityWithUpdate() {
Contract c = new Contract("gavin", "phone"); Contract c = new Contract("gavin", "phone");
ContractVariation cv1 = new ContractVariation(1, c); ContractVariation cv1 = new ContractVariation(1, c);
@ -108,7 +443,15 @@ public class ImmutableTest extends FunctionalTestCase {
t = s.beginTransaction(); t = s.beginTransaction();
c.setCustomerName("foo bar"); c.setCustomerName("foo bar");
s.update( c ); 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(); 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.close();
s = openSession(); s = openSession();
@ -145,7 +488,14 @@ public class ImmutableTest extends FunctionalTestCase {
cv1 = (ContractVariation) c.getVariations().iterator().next(); cv1 = (ContractVariation) c.getVariations().iterator().next();
cv1.setText("blah blah"); cv1.setText("blah blah");
s.update( c ); 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(); t.commit();
assertFalse( s.isReadOnly( c ) );
assertFalse( s.isReadOnly( cv1 ) );
assertFalse( s.isReadOnly( cv2 ) );
s.close(); s.close();
s = openSession(); s = openSession();
@ -224,7 +574,16 @@ public class ImmutableTest extends FunctionalTestCase {
s = openSession(); s = openSession();
t = s.beginTransaction(); t = s.beginTransaction();
c.setCustomerName("foo bar"); 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(); t.commit();
s.close(); s.close();
@ -233,7 +592,7 @@ public class ImmutableTest extends FunctionalTestCase {
c = (Contract) s.createCriteria(Contract.class).uniqueResult(); c = (Contract) s.createCriteria(Contract.class).uniqueResult();
assertEquals( c.getCustomerName(), "gavin" ); assertEquals( c.getCustomerName(), "gavin" );
assertEquals( c.getVariations().size(), 2 ); assertEquals( c.getVariations().size(), 2 );
Iterator it = c.getVariations().iterator(); it = c.getVariations().iterator();
cv1 = (ContractVariation) it.next(); cv1 = (ContractVariation) it.next();
assertEquals( cv1.getText(), "expensive" ); assertEquals( cv1.getText(), "expensive" );
cv2 = (ContractVariation) it.next(); cv2 = (ContractVariation) it.next();
@ -261,7 +620,16 @@ public class ImmutableTest extends FunctionalTestCase {
t = s.beginTransaction(); t = s.beginTransaction();
cv1 = (ContractVariation) c.getVariations().iterator().next(); cv1 = (ContractVariation) c.getVariations().iterator().next();
cv1.setText("blah blah"); 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(); t.commit();
s.close(); s.close();
@ -270,7 +638,7 @@ public class ImmutableTest extends FunctionalTestCase {
c = (Contract) s.createCriteria(Contract.class).uniqueResult(); c = (Contract) s.createCriteria(Contract.class).uniqueResult();
assertEquals( c.getCustomerName(), "gavin" ); assertEquals( c.getCustomerName(), "gavin" );
assertEquals( c.getVariations().size(), 2 ); assertEquals( c.getVariations().size(), 2 );
Iterator it = c.getVariations().iterator(); it = c.getVariations().iterator();
cv1 = (ContractVariation) it.next(); cv1 = (ContractVariation) it.next();
assertEquals( cv1.getText(), "expensive" ); assertEquals( cv1.getText(), "expensive" );
cv2 = (ContractVariation) it.next(); cv2 = (ContractVariation) it.next();

View File

@ -1,6 +1,9 @@
//$Id: GetLoadTest.java 10977 2006-12-12 23:28:04Z steve.ebersole@jboss.com $ //$Id: GetLoadTest.java 10977 2006-12-12 23:28:04Z steve.ebersole@jboss.com $
package org.hibernate.test.nonflushedchanges; package org.hibernate.test.nonflushedchanges;
import java.util.Iterator;
import java.util.List;
import junit.framework.Test; import junit.framework.Test;
import org.hibernate.Hibernate; import org.hibernate.Hibernate;
@ -8,6 +11,7 @@ import org.hibernate.Session;
import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.junit.functional.FunctionalTestClassTestSuite; import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.test.tm.SimpleJtaTransactionManagerImpl; import org.hibernate.test.tm.SimpleJtaTransactionManagerImpl;
@ -92,6 +96,131 @@ public class GetLoadTest extends AbstractOperationTestCase {
SimpleJtaTransactionManagerImpl.getInstance().commit(); SimpleJtaTransactionManagerImpl.getInstance().commit();
assertFetchCount( 0 ); 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 { public void testGetAfterDelete() throws Exception {

View File

@ -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;
}
}

View File

@ -21,4 +21,43 @@
<property name="description"/> <property name="description"/>
</class> </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> </hibernate-mapping>

View File

@ -2,15 +2,25 @@
package org.hibernate.test.readonly; package org.hibernate.test.readonly;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.io.Serializable;
/** /**
* @author Gavin King * @author Gavin King
*/ */
public class DataPoint { public class DataPoint implements Serializable {
private long id; private long id;
private BigDecimal x; private BigDecimal x;
private BigDecimal y; private BigDecimal y;
private String description; 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. * @return Returns the description.
*/ */
@ -59,4 +69,7 @@ public class DataPoint {
public void setY(BigDecimal y) { public void setY(BigDecimal y) {
this.y = y; this.y = y;
} }
void exception() throws Exception {
throw new Exception("foo");
}
} }

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,456 @@
//$Id: ReadOnlyTest.java 10977 2006-12-12 23:28:04Z steve.ebersole@jboss.com $
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.test.readonly;
import java.math.BigDecimal;
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();
}
}

View File

@ -26,6 +26,7 @@
package org.hibernate.test.readonly; package org.hibernate.test.readonly;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Iterator;
import java.util.List; import java.util.List;
import junit.framework.Test; import junit.framework.Test;
@ -44,6 +45,7 @@ import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
/** /**
* *
* @author Gavin King * @author Gavin King
* @author Gail Badner
*/ */
public class ReadOnlyTest extends FunctionalTestCase { public class ReadOnlyTest extends FunctionalTestCase {
@ -142,6 +144,31 @@ public class ReadOnlyTest extends FunctionalTestCase {
} }
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() { public void testReadOnlyRefreshFailureExpected() {
Session s = openSession(); Session s = openSession();
@ -209,6 +236,37 @@ public class ReadOnlyTest extends FunctionalTestCase {
} }
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() { public void testReadOnlyModeWithExistingModifiableEntity() {
Session s = openSession(); Session s = openSession();