[hibernate-commits] Hibernate SVN: r20872 - in core/branches/Branch_3_3_2_GA_CP: core/src/main/java/org/hibernate/dialect and 11 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Mon Nov 29 02:23:31 EST 2010


Author: stliu
Date: 2010-11-29 02:23:24 -0500 (Mon, 29 Nov 2010)
New Revision: 20872

Added:
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/AliasedTupleSubsetResultTransformer.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/CacheableResultTransformer.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/TupleSubsetResultTransformer.java
   core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/AbstractQueryCacheResultTransformerTest.java
   core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/Address.java
   core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/Course.java
   core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/CourseMeeting.java
   core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/CourseMeetingId.java
   core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/CriteriaQueryCacheIgnoreResultTransformerTest.java
   core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/CriteriaQueryCacheNormalResultTransformerTest.java
   core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/CriteriaQueryCachePutResultTransformerTest.java
   core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/Enrolment.hbm.xml
   core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/Enrolment.java
   core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/HqlQueryCacheIgnoreResultTransformerTest.java
   core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/HqlQueryCacheNormalResultTransformerTest.java
   core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/HqlQueryCachePutResultTransformerTest.java
   core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/Student.java
   core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/StudentDTO.java
Modified:
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/cache/QueryKey.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/cache/StandardQueryCache.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/dialect/HSQLDialect.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/hql/HolderInstantiator.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/hql/classic/QueryTranslatorImpl.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/Loader.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/custom/CustomLoader.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/hql/QueryLoader.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/property/Getter.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/property/PropertyAccessorFactory.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/AliasToBeanConstructorResultTransformer.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/AliasToBeanResultTransformer.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/AliasToEntityMapResultTransformer.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/DistinctResultTransformer.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/DistinctRootEntityResultTransformer.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/PassThroughResultTransformer.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/RootEntityResultTransformer.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/ToListResultTransformer.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/usertype/CompositeUserType.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/usertype/EnhancedUserType.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/usertype/LoggableUserType.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/usertype/UserCollectionType.java
   core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/usertype/UserType.java
   core/branches/Branch_3_3_2_GA_CP/core/src/test/java/org/hibernate/cache/QueryKeyTest.java
   core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/QueryCacheTest.java
Log:
JBPAPP-4175 JBPAPP-5478 JBPAPP-5479 HHH-5163 HHH-4011 HHH-2510 a big back port to fix the bug of applying a resultTransformer on a chchable projection based criteria

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/cache/QueryKey.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/cache/QueryKey.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/cache/QueryKey.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -34,7 +34,7 @@
 import org.hibernate.engine.RowSelection;
 import org.hibernate.engine.SessionImplementor;
 import org.hibernate.engine.TypedValue;
-import org.hibernate.transform.ResultTransformer;
+import org.hibernate.transform.CacheableResultTransformer;
 import org.hibernate.type.Type;
 import org.hibernate.util.CollectionHelper;
 import org.hibernate.util.EqualsHelper;
@@ -58,7 +58,7 @@
 
 	// the user provided resulttransformer, not the one used with "select new". Here to avoid mangling
 	// transformed/non-transformed results.
-	private final ResultTransformer customTransformer;
+	private final CacheableResultTransformer customTransformer;
 
 	/**
 	 * For performance reasons, the hashCode is cached; however, it is marked transient so that it can be
@@ -80,7 +80,8 @@
 			String queryString,
 			QueryParameters queryParameters,
 			Set filterKeys,
-			SessionImplementor session) {
+			SessionImplementor session,
+			CacheableResultTransformer customTransformer) {
 		// disassemble positional parameters
 		final int positionalParameterCount = queryParameters.getPositionalParameterTypes().length;
 		final Type[] types = new Type[positionalParameterCount];
@@ -134,7 +135,7 @@
 				maxRows,
 				filterKeys,
 				session.getEntityMode(),
-				queryParameters.getResultTransformer()
+				customTransformer
 		);
 	}
 
@@ -160,7 +161,7 @@
 			Integer maxRows,
 			Set filterKeys,
 			EntityMode entityMode,
-			ResultTransformer customTransformer) {
+			CacheableResultTransformer customTransformer) {
 		this.sqlQueryString = sqlQueryString;
 		this.positionalParameterTypes = positionalParameterTypes;
 		this.positionalParameterValues = positionalParameterValues;
@@ -172,6 +173,10 @@
 		this.customTransformer = customTransformer;
 		this.hashCode = generateHashCode();
 	}
+	
+	public CacheableResultTransformer getResultTransformer() {
+		return customTransformer;
+	}
 
 	/**
 	 * Deserialization hook used to re-init the cached hashcode which is needed for proper clustering support.

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/cache/StandardQueryCache.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/cache/StandardQueryCache.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/cache/StandardQueryCache.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -94,6 +94,9 @@
 			}
 
 			List cacheable = new ArrayList( result.size() + 1 );
+			if ( log.isTraceEnabled() ) {
+				logCachedResultDetails( key, null, returnTypes, cacheable );
+			}
 			cacheable.add( ts );
 			for ( int i = 0; i < result.size(); i++ ) {
 				if ( returnTypes.length == 1 ) {
@@ -106,6 +109,9 @@
 							)
 					);
 				}
+				if ( log.isTraceEnabled() ) {
+					logCachedResultRowDetails( returnTypes, result.get( i ) );
+				}
 			}
 
 			cacheRegion.put( key, cacheable );
@@ -127,6 +133,10 @@
 		}
 
 		List cacheable = ( List ) cacheRegion.get( key );
+		if ( log.isTraceEnabled() ) {
+			logCachedResultDetails( key, spaces, returnTypes, cacheable );
+		}
+
 		if ( cacheable == null ) {
 			log.debug( "query results were not found in cache" );
 			return null;
@@ -160,6 +170,9 @@
 							)
 					);
 				}
+				if ( log.isTraceEnabled() ) {
+					logCachedResultRowDetails( returnTypes, result.get( i - 1 ));
+				}
 			}
 			catch ( UnresolvableObjectException uoe ) {
 				if ( isNaturalKeyLookup ) {
@@ -202,5 +215,72 @@
 	public String toString() {
 		return "StandardQueryCache(" + cacheRegion.getName() + ')';
 	}
+	
+	private static void logCachedResultDetails(QueryKey key, Set querySpaces, Type[] returnTypes, List result) {
+		if ( ! log.isTraceEnabled() ) {
+			return;
+		}
+		log.trace( "key.hashCode="+key.hashCode() );
+		log.trace( "querySpaces="+querySpaces );
+		if ( returnTypes == null || returnTypes.length == 0 ) {
+				log.trace( "unexpected returnTypes is "+( returnTypes == null ? "null" : "empty" )+
+						"! result"+( result == null ? " is null": ".size()=" + result.size() ) );
+		}
+		else {
+			StringBuffer returnTypeInfo = new StringBuffer();
+			for ( int i=0; i<returnTypes.length; i++ ) {
+				returnTypeInfo.append( "typename=" )
+						.append( returnTypes[ i ].getName() )
+						.append(" class=" )
+						.append( returnTypes[ i ].getReturnedClass().getName() ).append(' ');
+			}
+			log.trace( " returnTypeInfo="+returnTypeInfo );
+		}
+	}
 
+	private static void logCachedResultRowDetails(Type[] returnTypes, Object result) {
+		if ( ! log.isTraceEnabled() ) {
+			return;
+		}
+		logCachedResultRowDetails(
+				returnTypes,
+				( result instanceof Object[] ? ( Object[] ) result : new Object[] { result } )
+		);
+	}
+
+	private static void logCachedResultRowDetails(Type[] returnTypes, Object[] tuple) {
+		if ( ! log.isTraceEnabled() ) {
+			return;
+		}
+		if ( tuple == null ) {
+			log.trace( " tuple is null; returnTypes is "+( returnTypes == null ? "null" : "Type["+returnTypes.length+"]" ) );
+			if ( returnTypes != null && returnTypes.length > 1 ) {
+				log.trace( "unexpected result tuple! "+
+						"tuple is null; should be Object["+returnTypes.length+"]!" );
+			}
+		}
+		else {
+			if ( returnTypes == null || returnTypes.length == 0 ) {
+				log.trace( "unexpected result tuple! "+
+						"tuple is non-null; returnTypes is "+( returnTypes == null ? "null" : "empty" ) );
+			}
+			log.trace( " tuple is Object["+tuple.length+
+					"]; returnTypes is Type["+returnTypes.length+"]" );
+			if ( tuple.length != returnTypes.length ) {
+				log.trace( "unexpected tuple length! transformer="+
+					" expected="+returnTypes.length+
+					" got="+tuple.length );
+			}
+			else {
+				for ( int j = 0; j < tuple.length; j++ ) {
+					if ( tuple[ j ] != null && ! returnTypes[ j ].getReturnedClass().isInstance( tuple[ j ] ) ) {
+						log.trace( "unexpected tuple value type! transformer="+
+								" expected="+returnTypes[ j ].getReturnedClass().getName()+
+								" got="+tuple[ j ].getClass().getName() );
+					}
+				}
+			}
+		}
+	}
+
 }

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/dialect/HSQLDialect.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/dialect/HSQLDialect.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/dialect/HSQLDialect.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -172,7 +172,7 @@
 	public String getIdentityInsertString() {
 		return "null";
 	}
-
+	
 	public String getForUpdateString() {
 		return "";
 	}

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/hql/HolderInstantiator.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/hql/HolderInstantiator.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/hql/HolderInstantiator.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -41,13 +41,16 @@
 	private final String[] queryReturnAliases;
 	
 	public static HolderInstantiator getHolderInstantiator(ResultTransformer selectNewTransformer, ResultTransformer customTransformer, String[] queryReturnAliases) {
-		if(selectNewTransformer!=null) {
-			return new HolderInstantiator(selectNewTransformer, queryReturnAliases);
-		} else {
-			return new HolderInstantiator(customTransformer, queryReturnAliases);
-		}
+		return new HolderInstantiator(
+				resolveResultTransformer( selectNewTransformer, customTransformer ),
+				queryReturnAliases
+		);
 	}
 	
+	public static ResultTransformer resolveResultTransformer(ResultTransformer selectNewTransformer, ResultTransformer customTransformer) {
+		return selectNewTransformer != null ? selectNewTransformer : customTransformer;
+	}	
+	
 	public static ResultTransformer createSelectNewTransformer(Constructor constructor, boolean returnMaps, boolean returnLists) {
 		if ( constructor != null ) {
 			return new AliasToBeanConstructorResultTransformer(constructor);
@@ -65,13 +68,14 @@
 	
 	static public HolderInstantiator createClassicHolderInstantiator(Constructor constructor, 
 			ResultTransformer transformer) {
-		if ( constructor != null ) {
-			return new HolderInstantiator(new AliasToBeanConstructorResultTransformer(constructor), null);
-		}
-		else {
-			return new HolderInstantiator(transformer, null);
-		}
+		return new HolderInstantiator( resolveClassicResultTransformer( constructor, transformer ), null );
 	}
+
+	static public ResultTransformer resolveClassicResultTransformer(
+			Constructor constructor,
+			ResultTransformer transformer) {
+		return constructor != null ? new AliasToBeanConstructorResultTransformer( constructor ) : transformer;
+	}	
 	
 	public HolderInstantiator( 
 			ResultTransformer transformer,
@@ -86,11 +90,7 @@
 	}
 	
 	public Object instantiate(Object[] row) {
-		if(transformer==null) {
-			return row;
-		} else {
-			return transformer.transformTuple(row, queryReturnAliases);
-		}
+		return transformer == null ? row : transformer.transformTuple(row, queryReturnAliases);
 	}	
 	
 	public String[] getQueryReturnAliases() {

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/hql/classic/QueryTranslatorImpl.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/hql/classic/QueryTranslatorImpl.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/hql/classic/QueryTranslatorImpl.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -30,6 +30,7 @@
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -977,29 +978,25 @@
 		throw new UnsupportedOperationException( "Not supported!  Use the AST translator...");
 	}
 
-	protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session)
+	protected Object getResultColumnOrRow( Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session )
 			throws SQLException, HibernateException {
-		row = toResultRow( row );
+		Object[] resultRow = getResultRow( row, rs, session );
+		return (holderClass == null && resultRow.length == 1 ? resultRow[0] : resultRow);
+	}
+
+	protected Object[] getResultRow( Object[] row, ResultSet rs, SessionImplementor session ) throws SQLException, HibernateException {
+		Object[] resultRow;
 		if ( hasScalars ) {
 			String[][] scalarColumns = getColumnNames();
 			int queryCols = returnTypes.length;
-			if ( holderClass == null && queryCols == 1 ) {
-				return returnTypes[0].nullSafeGet( rs, scalarColumns[0], session, null );
+			resultRow = new Object[queryCols];
+			for ( int i = 0; i < queryCols; i++ ) {
+				resultRow[i] = returnTypes[i].nullSafeGet( rs, scalarColumns[i], session, null );
 			}
-			else {
-				row = new Object[queryCols];
-				for ( int i = 0; i < queryCols; i++ )
-					row[i] = returnTypes[i].nullSafeGet( rs, scalarColumns[i], session, null );
-				return row;
-			}
+		} else {
+			resultRow = toResultRow( row );
 		}
-		else if ( holderClass == null ) {
-			return row.length == 1 ? row[0] : row;
-		}
-		else {
-			return row;
-		}
-
+		return resultRow;
 	}
 
 	protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {
@@ -1030,6 +1027,20 @@
 			return result;
 		}
 	}
+	protected boolean[] includeInResultRow() {
+		boolean[] isResultReturned = includeInSelect;
+		if ( hasScalars ) {
+			isResultReturned = new boolean[ returnedTypes.size() ];
+			Arrays.fill( isResultReturned, true );
+		}
+		return isResultReturned;
+	}
+	protected ResultTransformer resolveResultTransformer(ResultTransformer resultTransformer) {
+		return HolderInstantiator.resolveClassicResultTransformer(
+				holderConstructor,
+				resultTransformer
+		);
+	}
 
 	void setHolderClass(Class clazz) {
 		holderClass = clazz;

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/Loader.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/Loader.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/Loader.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -75,6 +75,7 @@
 import org.hibernate.persister.entity.UniqueKeyLoadable;
 import org.hibernate.pretty.MessageHelper;
 import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.transform.CacheableResultTransformer;
 import org.hibernate.transform.ResultTransformer;
 import org.hibernate.type.AssociationType;
 import org.hibernate.type.EntityType;
@@ -172,6 +173,10 @@
 	protected int[] getCollectionOwners() {
 		return null;
 	}
+	
+	protected int[][] getCompositeKeyManyToOneTargetIndices() {
+		return null;
+	}
 
 	/**
 	 * What lock mode does this load entities with?
@@ -241,21 +246,56 @@
 		}
 	}
 
+//	/**
+//	 * Execute an SQL query and attempt to instantiate instances of the class mapped by the given
+//	 * persister from each row of the <tt>ResultSet</tt>. If an object is supplied, will attempt to
+//	 * initialize that object. If a collection is supplied, attempt to initialize that collection.
+//	 */
+//	private List doQueryAndInitializeNonLazyCollections(final SessionImplementor session,
+//														final QueryParameters queryParameters,
+//														final boolean returnProxies) 
+//		throws HibernateException, SQLException {
+//
+//		final PersistenceContext persistenceContext = session.getPersistenceContext();
+//		persistenceContext.beforeLoad();
+//		List result;
+//		try {
+//			result = doQuery( session, queryParameters, returnProxies );
+//		}
+//		finally {
+//			persistenceContext.afterLoad();
+//		}
+//		persistenceContext.initializeNonLazyCollections();
+//		return result;
+//	}
 	/**
 	 * Execute an SQL query and attempt to instantiate instances of the class mapped by the given
 	 * persister from each row of the <tt>ResultSet</tt>. If an object is supplied, will attempt to
 	 * initialize that object. If a collection is supplied, attempt to initialize that collection.
 	 */
-	private List doQueryAndInitializeNonLazyCollections(final SessionImplementor session,
-														final QueryParameters queryParameters,
-														final boolean returnProxies) 
-		throws HibernateException, SQLException {
+	private List doQueryAndInitializeNonLazyCollections(
+			final SessionImplementor session,
+			final QueryParameters queryParameters,
+			final boolean returnProxies) throws HibernateException, SQLException {
+		return doQueryAndInitializeNonLazyCollections(
+				session,
+				queryParameters,
+				returnProxies,
+				null
+		);
+	}
 
+	private List doQueryAndInitializeNonLazyCollections(
+			final SessionImplementor session,
+			final QueryParameters queryParameters,
+			final boolean returnProxies,
+			final ResultTransformer forcedResultTransformer)
+			throws HibernateException, SQLException {
 		final PersistenceContext persistenceContext = session.getPersistenceContext();
 		persistenceContext.beforeLoad();
 		List result;
 		try {
-			result = doQuery( session, queryParameters, returnProxies );
+			result = doQuery( session, queryParameters, returnProxies, forcedResultTransformer );
 		}
 		finally {
 			persistenceContext.afterLoad();
@@ -569,7 +609,6 @@
 		}
 
 	}
-
 	private Object getRowFromResultSet(
 	        final ResultSet resultSet,
 	        final SessionImplementor session,
@@ -579,23 +618,23 @@
 	        final List hydratedObjects,
 	        final EntityKey[] keys,
 	        boolean returnProxies) throws SQLException, HibernateException {
+		return getRowFromResultSet( resultSet, session, queryParameters, lockModeArray, optionalObjectKey, hydratedObjects, keys, returnProxies, null );
+		
+	}
+	private Object getRowFromResultSet(
+	        final ResultSet resultSet,
+	        final SessionImplementor session,
+	        final QueryParameters queryParameters,
+	        final LockMode[] lockModeArray,
+	        final EntityKey optionalObjectKey,
+	        final List hydratedObjects,
+	        final EntityKey[] keys,
+	        boolean returnProxies,
+	        ResultTransformer forcedResultTransformer) throws SQLException, HibernateException {
 
 		final Loadable[] persisters = getEntityPersisters();
 		final int entitySpan = persisters.length;
-
-		for ( int i = 0; i < entitySpan; i++ ) {
-			keys[i] = getKeyFromResultSet(
-			        i,
-					persisters[i],
-					i == entitySpan - 1 ?
-							queryParameters.getOptionalId() :
-							null,
-					resultSet,
-					session
-				);
-			//TODO: the i==entitySpan-1 bit depends upon subclass implementation (very bad)
-		}
-
+		extractKeysFromResultSet( persisters, queryParameters, resultSet, session, keys, lockModeArray, hydratedObjects );
 		registerNonExists( keys, persisters, session );
 
 		// this call is side-effecty
@@ -625,10 +664,112 @@
 			}
 		}
 
-		return getResultColumnOrRow( row, queryParameters.getResultTransformer(), resultSet, session );
+		return forcedResultTransformer == null ?
+				getResultColumnOrRow( row, queryParameters.getResultTransformer(), resultSet, session ) :
+				forcedResultTransformer.transformTuple(
+						getResultRow( row, resultSet, session ),
+						getResultRowAliases()
+				)
+		;
 
 	}
 
+	protected Object[] getResultRow( Object[] row, ResultSet rs, SessionImplementor session ) throws SQLException, HibernateException {
+		return row;
+	}
+
+	protected void extractKeysFromResultSet(
+			Loadable[] persisters,
+			QueryParameters queryParameters,
+			ResultSet resultSet,
+			SessionImplementor session,
+			EntityKey[] keys,
+			LockMode[] lockModes,
+			List hydratedObjects) throws SQLException {
+		final int entitySpan = persisters.length;
+
+		final int numberOfPersistersToProcess;
+		final Serializable optionalId = queryParameters.getOptionalId();
+		if ( isSingleRowLoader() && optionalId != null ) {
+			keys[ entitySpan - 1 ] = new EntityKey( optionalId, persisters[ entitySpan - 1 ], session.getEntityMode() );
+			// skip the last persister below...
+			numberOfPersistersToProcess = entitySpan - 1;
+		}
+		else {
+			numberOfPersistersToProcess = entitySpan;
+		}
+
+		final Object[] hydratedKeyState = new Object[numberOfPersistersToProcess];
+
+		for ( int i = 0; i < numberOfPersistersToProcess; i++ ) {
+			final Type idType = persisters[i].getIdentifierType();
+			hydratedKeyState[i] = idType.hydrate( resultSet, getEntityAliases()[i].getSuffixedKeyAliases(), session, null );
+		}
+
+		for ( int i = 0; i < numberOfPersistersToProcess; i++ ) {
+			final Type idType = persisters[i].getIdentifierType();
+			if ( idType.isComponentType() && getCompositeKeyManyToOneTargetIndices() != null ) {
+				// we may need to force resolve any key-many-to-one(s)
+				int[] keyManyToOneTargetIndices = getCompositeKeyManyToOneTargetIndices()[i];
+				// todo : better solution is to order the index processing based on target indices
+				//		that would account for multiple levels whereas this scheme does not
+				if ( keyManyToOneTargetIndices != null ) {
+					for ( int targetIndex : keyManyToOneTargetIndices ) {
+						if ( targetIndex < numberOfPersistersToProcess ) {
+							final Type targetIdType = persisters[targetIndex].getIdentifierType();
+							final Serializable targetId = (Serializable) targetIdType.resolve(
+									hydratedKeyState[targetIndex],
+									session,
+									null
+							);
+							// todo : need a way to signal that this key is resolved and its data resolved
+							keys[targetIndex] = new EntityKey( targetId, persisters[targetIndex], session.getEntityMode() );
+						}
+
+						// this part copied from #getRow, this section could be refactored out
+						Object object = session.getEntityUsingInterceptor( keys[targetIndex] );
+						if ( object != null ) {
+							//its already loaded so don't need to hydrate it
+							instanceAlreadyLoaded(
+									resultSet,
+									targetIndex,
+									persisters[targetIndex],
+									keys[targetIndex],
+									object,
+									lockModes[targetIndex],
+									session
+							);
+						}
+						else {
+							object = instanceNotYetLoaded(
+									resultSet,
+									targetIndex,
+									persisters[targetIndex],
+									getEntityAliases()[targetIndex].getRowIdAlias(),
+									keys[targetIndex],
+									lockModes[targetIndex],
+									getOptionalObjectKey( queryParameters, session ),
+									queryParameters.getOptionalObject(),
+									hydratedObjects,
+									session
+							);
+						}
+					}
+				}
+			}
+			final Serializable resolvedId = (Serializable) idType.resolve( hydratedKeyState[i], session, null );
+			keys[i] = resolvedId == null ? null : new EntityKey( resolvedId, persisters[i], session.getEntityMode() );
+		}
+	}
+
+	private Serializable determineResultId(SessionImplementor session, Serializable optionalId, Type idType, Serializable resolvedId) {
+		final boolean idIsResultId = optionalId != null
+				&& resolvedId != null
+				&& idType.isEqual( optionalId, resolvedId, session.getEntityMode(), factory );
+		final Serializable resultId = idIsResultId ? optionalId : resolvedId;
+		return resultId;
+	}
+
 	/**
 	 * Read any collection elements contained in a single row of the result set
 	 */
@@ -682,7 +823,8 @@
 	private List doQuery(
 			final SessionImplementor session,
 			final QueryParameters queryParameters,
-			final boolean returnProxies) throws SQLException, HibernateException {
+			final boolean returnProxies,
+			final ResultTransformer forcedResultTransformer) throws SQLException, HibernateException {
 
 		final RowSelection selection = queryParameters.getRowSelection();
 		final int maxRows = hasMaxRows( selection ) ?
@@ -728,7 +870,8 @@
 						optionalObjectKey,
 						hydratedObjects,
 						keys,
-						returnProxies 
+						returnProxies,
+						forcedResultTransformer
 				);
 				results.add( result );
 
@@ -902,11 +1045,42 @@
 				.endLoadingCollections( collectionPersister );
 	}
 
+	/**
+	 * Determine the actual ResultTransformer that will be used to
+	 * transform query results.
+	 *
+	 * @param resultTransformer the specified result transformer
+	 * @return the actual result transformer
+	 */
+	protected ResultTransformer resolveResultTransformer(ResultTransformer resultTransformer) {
+		return resultTransformer;
+	}
+
 	protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {
 		return results;
 	}
 
 	/**
+	 * Are rows transformed immediately after being read from the ResultSet?
+	 * @return true, if getResultColumnOrRow() transforms the results; false, otherwise
+	 */
+	protected boolean areResultSetRowsTransformedImmediately() {
+		return false;
+	}
+	
+	/**
+	 * Returns the aliases that corresponding to a result row.
+	 * @return Returns the aliases that corresponding to a result row.
+	 */
+	protected String[] getResultRowAliases() {
+		 return null;
+	}
+
+	protected boolean[] includeInResultRow() {
+		return null;
+	}
+
+	/**
 	 * Get the actual object that is returned in the user-visible result list.
 	 * This empty implementation merely returns its first argument. This is
 	 * overridden by some subclasses.
@@ -2138,17 +2312,15 @@
 	
 		QueryCache queryCache = factory.getQueryCache( queryParameters.getCacheRegion() );
 		
-		Set filterKeys = FilterKey.createFilterKeys( 
-				session.getEnabledFilters(),
-				session.getEntityMode() 
-		);
-		QueryKey key = QueryKey.generateQueryKey(
-				getSQLString(), 
-				queryParameters, 
-				filterKeys, 
-				session
-		);
 		
+		QueryKey key = generateQueryKey( session, queryParameters );
+		if ( querySpaces == null || querySpaces.size() == 0 ) {
+			log.trace( "unexpected querySpaces is "+( querySpaces == null ? "null" : "empty" ) );
+		}
+		else {
+			log.trace( "querySpaces is "+querySpaces.toString() );
+		}
+
 		List result = getResultFromQueryCache( 
 				session, 
 				queryParameters, 
@@ -2159,7 +2331,7 @@
 		);
 
 		if ( result == null ) {
-			result = doList( session, queryParameters );
+			result = doList( session, queryParameters, key.getResultTransformer() );
 
 			putResultInQueryCache( 
 					session, 
@@ -2170,10 +2342,49 @@
 					result 
 			);
 		}
+		
+		ResultTransformer resolvedTransformer = resolveResultTransformer( queryParameters.getResultTransformer() );
+		if ( resolvedTransformer != null ) {
+			result = (
+					areResultSetRowsTransformedImmediately() ?
+							key.getResultTransformer().retransformResults(
+									result,
+									getResultRowAliases(),
+									queryParameters.getResultTransformer(),
+									includeInResultRow()
+							) :
+							key.getResultTransformer().untransformToTuples(
+									result
+							)
+			);
+		}
 
 		return getResultList( result, queryParameters.getResultTransformer() );
 	}
+	
+	private QueryKey generateQueryKey(
+			SessionImplementor session,
+			QueryParameters queryParameters) {
+		return QueryKey.generateQueryKey(
+				getSQLString(),
+				queryParameters,
+				FilterKey.createFilterKeys(
+						session.getEnabledFilters(),
+						session.getEntityMode()
+				),
+				session,
+				createCacheableResultTransformer( queryParameters )
+		);
+	}
 
+	private CacheableResultTransformer createCacheableResultTransformer(QueryParameters queryParameters) {
+		return CacheableResultTransformer.create(
+				queryParameters.getResultTransformer(),
+				getResultRowAliases(),
+				includeInResultRow()
+		);
+	}
+
 	private List getResultFromQueryCache(
 			final SessionImplementor session,
 			final QueryParameters queryParameters,
@@ -2186,7 +2397,7 @@
 		if ( session.getCacheMode().isGetEnabled() ) {
 			boolean isImmutableNaturalKeyLookup = queryParameters.isNaturalKeyLookup()
 					&& getEntityPersisters()[0].getEntityMetamodel().hasImmutableNaturalId();
-			result = queryCache.get( key, resultTypes, isImmutableNaturalKeyLookup, querySpaces, session );
+			result = queryCache.get( key, key.getResultTransformer().getCachedResultTypes( resultTypes ), isImmutableNaturalKeyLookup, querySpaces, session );
 			if ( factory.getStatistics().isStatisticsEnabled() ) {
 				if ( result == null ) {
 					factory.getStatisticsImplementor()
@@ -2202,26 +2413,25 @@
 		return result;
 	}
 
-	private void putResultInQueryCache(
-			final SessionImplementor session,
-			final QueryParameters queryParameters,
-			final Type[] resultTypes,
-			final QueryCache queryCache,
-			final QueryKey key,
-			final List result) {
+	private void putResultInQueryCache( final SessionImplementor session, final QueryParameters queryParameters, final Type[] resultTypes,
+			final QueryCache queryCache, final QueryKey key, final List result ) {
 		if ( session.getCacheMode().isPutEnabled() ) {
-			boolean put = queryCache.put( key, resultTypes, result, queryParameters.isNaturalKeyLookup(), session );
+			boolean put = queryCache.put( key, key.getResultTransformer().getCachedResultTypes( resultTypes ), result,
+					queryParameters.isNaturalKeyLookup(), session );
 			if ( put && factory.getStatistics().isStatisticsEnabled() ) {
-				factory.getStatisticsImplementor()
-						.queryCachePut( getQueryIdentifier(), queryCache.getRegion().getName() );
+				factory.getStatisticsImplementor().queryCachePut( getQueryIdentifier(), queryCache.getRegion().getName() );
 			}
 		}
 	}
 
+	protected List doList( final SessionImplementor session, final QueryParameters queryParameters ) throws HibernateException {
+		return doList( session, queryParameters, null );
+	}
+
 	/**
 	 * Actually execute a query, ignoring the query cache
 	 */
-	protected List doList(final SessionImplementor session, final QueryParameters queryParameters)
+	protected List doList(final SessionImplementor session, final QueryParameters queryParameters, final ResultTransformer forcedResultTransformer)
 			throws HibernateException {
 
 		final boolean stats = getFactory().getStatistics().isStatisticsEnabled();
@@ -2230,7 +2440,7 @@
 
 		List result;
 		try {
-			result = doQueryAndInitializeNonLazyCollections( session, queryParameters, true );
+			result = doQueryAndInitializeNonLazyCollections( session, queryParameters, true, forcedResultTransformer );
 		}
 		catch ( SQLException sqle ) {
 			throw JDBCExceptionHelper.convert(

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -25,6 +25,7 @@
 package org.hibernate.loader.criteria;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -59,10 +60,13 @@
 	private final CriteriaQueryTranslator translator;
 	private final Set querySpaces;
 	private final Type[] resultTypes;
+	private final boolean[] includeInResultRow;
 	//the user visible aliases, which are unknown to the superclass,
 	//these are not the actual "physical" SQL aliases
 	private final String[] userAliases;
 	private final List userAliasList = new ArrayList();
+	private final List resultTypeList = new ArrayList();
+	private final List includeInResultRowList = new ArrayList();
 
 	public Type[] getResultTypes() {
 		return resultTypes;
@@ -71,6 +75,10 @@
 	public String[] getUserAliases() {
 		return userAliases;
 	}
+	
+	public boolean[] includeInResultRow() {
+		return includeInResultRow;
+	}
 
 	public CriteriaJoinWalker(
 			final OuterJoinLoadable persister, 
@@ -97,8 +105,6 @@
 		querySpaces = translator.getQuerySpaces();
 
 		if ( translator.hasProjection() ) {
-			resultTypes = translator.getProjectedTypes();
-			
 			initProjection( 
 					translator.getSelect(), 
 					translator.getWhereCondition(), 
@@ -106,16 +112,22 @@
 					translator.getGroupBy(),
 					LockMode.NONE 
 				);
+			resultTypes = translator.getProjectedTypes();
+			userAliases = translator.getProjectedAliases();
+			includeInResultRow = new boolean[ resultTypes.length ];
+			Arrays.fill(  includeInResultRow, true );
 		}
 		else {
-			resultTypes = new Type[] { TypeFactory.manyToOne( persister.getEntityName() ) };
-
 			initAll( translator.getWhereCondition(), translator.getOrderBy(), LockMode.NONE );
+			// root entity comes last
+			userAliasList.add( criteria.getAlias() ); //root entity comes *last*
+			resultTypeList.add( translator.getResultType( criteria ) );
+			includeInResultRowList.add( true );
+			userAliases = ArrayHelper.toStringArray( userAliasList );
+			resultTypes = ArrayHelper.toTypeArray( resultTypeList );
+			includeInResultRow = ArrayHelper.toBooleanArray( includeInResultRowList );
+			
 		}
-		
-		userAliasList.add( criteria.getAlias() ); //root entity comes *last*
-		userAliases = ArrayHelper.toStringArray(userAliasList);
-
 	}
 
 	protected int getJoinType(
@@ -180,11 +192,20 @@
 			final Criteria subcriteria = translator.getCriteria(path);
 			String sqlAlias = subcriteria==null ? null : translator.getSQLAlias(subcriteria);
 			if (sqlAlias!=null) {
-				userAliasList.add( subcriteria.getAlias() ); //alias may be null
+//				userAliasList.add( subcriteria.getAlias() ); //alias may be null
+				if( !translator.hasProjection() ){
+					includeInResultRowList.add( subcriteria.getAlias() != null );
+					if( subcriteria.getAlias() != null ){
+						userAliasList.add( subcriteria.getAlias() );
+						resultTypeList.add( translator.getResultType( subcriteria ) );
+					}
+				}
 				return sqlAlias; //EARLY EXIT
 			}
 			else {
-				userAliasList.add(null);
+				if ( ! translator.hasProjection() ) {
+					includeInResultRowList.add( false );
+				}
 			}
 		}
 		return super.generateTableAlias( n + translator.getSQLAliasCount(), path, joinable );

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -46,6 +46,7 @@
 import org.hibernate.persister.entity.OuterJoinLoadable;
 import org.hibernate.transform.ResultTransformer;
 import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
 
 /**
  * A <tt>Loader</tt> for <tt>Criteria</tt> queries. Note that criteria queries are
@@ -68,6 +69,8 @@
 	//the user visible aliases, which are unknown to the superclass,
 	//these are not the actual "physical" SQL aliases
 	private final String[] userAliases;
+	private final boolean[] includeInResultRow;
+	private final int resultRowLength;
 
 	public CriteriaLoader(
 			final OuterJoinLoadable persister, 
@@ -100,7 +103,8 @@
 		
 		userAliases = walker.getUserAliases();
 		resultTypes = walker.getResultTypes();
-
+		includeInResultRow = walker.includeInResultRow();
+		resultRowLength = ArrayHelper.countTrue( includeInResultRow );
 		postInstantiate();
 
 	}
@@ -120,8 +124,15 @@
 
 	protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session)
 	throws SQLException, HibernateException {
+		return resolveResultTransformer( transformer ).transformTuple(
+				getResultRow( row, rs, session),
+				getResultRowAliases()
+		);
+	}
+	
+	protected Object[] getResultRow(Object[] row, ResultSet rs, SessionImplementor session)
+	throws SQLException, HibernateException {
 		final Object[] result;
-		final String[] aliases;
 		if ( translator.hasProjection() ) {
 			Type[] types = translator.getProjectedTypes();
 			result = new Object[types.length];
@@ -129,20 +140,45 @@
 			for ( int i=0; i<result.length; i++ ) {
 				result[i] = types[i].nullSafeGet(rs, columnAliases[i], session, null);
 			}
-			aliases = translator.getProjectedAliases();
 		}
 		else {
-			result = row;
-			aliases = userAliases;
+			result = toResultRow( row );
 		}
-		return translator.getRootCriteria().getResultTransformer()
-				.transformTuple(result, aliases);
+		return result;
 	}
 
+	private Object[] toResultRow(Object[] row) {
+		if ( resultRowLength == row.length ) {
+			return row;
+		}
+		else {
+			Object[] result = new Object[ resultRowLength ];
+			int j = 0;
+			for ( int i = 0; i < row.length; i++ ) {
+				if ( includeInResultRow[i] ) result[j++] = row[i];
+			}
+			return result;
+		}
+	}
+	
 	public Set getQuerySpaces() {
 		return querySpaces;
 	}
+	
+	protected String[] getResultRowAliases() {
+		return userAliases;
+	}
+	
+	protected ResultTransformer resolveResultTransformer(ResultTransformer resultTransformer) {
+		return translator.getRootCriteria().getResultTransformer();
+	}
 
+	protected boolean areResultSetRowsTransformedImmediately() {
+		return true;
+	}
+	protected boolean[] includeInResultRow() {
+		return includeInResultRow;
+	}
 	protected String applyLocks(String sqlSelectString, Map lockModes, Dialect dialect) throws QueryException {
 		if ( lockModes == null || lockModes.isEmpty() ) {
 			return sqlSelectString;
@@ -184,7 +220,7 @@
 	}
 	
 	protected List getResultList(List results, ResultTransformer resultTransformer) {
-		return translator.getRootCriteria().getResultTransformer().transformList( results );
+		return resolveResultTransformer( resultTransformer ).transformList( results );
 	}
 
 }

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -55,6 +55,7 @@
 import org.hibernate.type.AssociationType;
 import org.hibernate.type.NullableType;
 import org.hibernate.type.Type;
+import org.hibernate.type.TypeFactory;
 import org.hibernate.util.ArrayHelper;
 import org.hibernate.util.StringHelper;
 
@@ -336,6 +337,11 @@
 		        this
 		);
 	}
+	
+	/* package-protected */
+	Type getResultType(Criteria criteria) {
+		return TypeFactory.manyToOne( getEntityName( criteria ) );
+	}
 
 	public Type[] getProjectedTypes() {
 		return rootCriteria.getProjection().getTypes( rootCriteria, this );

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/custom/CustomLoader.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/custom/CustomLoader.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/custom/CustomLoader.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -79,6 +79,7 @@
 	private final CollectionAliases[] collectionAliases;
 
 	private final LockMode[] lockModes;
+	private boolean[] includeInResultRow;
 //	private final String[] sqlAliases;
 //	private final String[] sqlAliasSuffixes;
 	private final ResultRowProcessor rowProcessor;
@@ -113,7 +114,9 @@
 		List specifiedAliases = new ArrayList();
 		int returnableCounter = 0;
 		boolean hasScalars = false;
-
+		
+		List includeInResultRowList = new ArrayList();
+		
 		Iterator itr = customQuery.getCustomQueryReturns().iterator();
 		while ( itr.hasNext() ) {
 			final Return rtn = ( Return ) itr.next();
@@ -127,6 +130,7 @@
 								scalarRtn.getType()
 						)
 				);
+				includeInResultRowList.add( true );
 				hasScalars = true;
 			}
 			else if ( rtn instanceof RootReturn ) {
@@ -141,6 +145,7 @@
 				specifiedAliases.add( rootRtn.getAlias() );
 				entityAliases.add( rootRtn.getEntityAliases() );
 				ArrayHelper.addAll( querySpaces, persister.getQuerySpaces() );
+				includeInResultRowList.add( true );
 			}
 			else if ( rtn instanceof CollectionReturn ) {
 				CollectionReturn collRtn = ( CollectionReturn ) rtn;
@@ -163,6 +168,7 @@
 					entityAliases.add( collRtn.getElementEntityAliases() );
 					ArrayHelper.addAll( querySpaces, elementPersister.getQuerySpaces() );
 				}
+				includeInResultRowList.add( true );
 			}
 			else if ( rtn instanceof EntityFetchReturn ) {
 				EntityFetchReturn fetchRtn = ( EntityFetchReturn ) rtn;
@@ -179,6 +185,7 @@
 				specifiedAliases.add( fetchRtn.getAlias() );
 				entityAliases.add( fetchRtn.getEntityAliases() );
 				ArrayHelper.addAll( querySpaces, persister.getQuerySpaces() );
+				includeInResultRowList.add( false );
 			}
 			else if ( rtn instanceof CollectionFetchReturn ) {
 				CollectionFetchReturn fetchRtn = ( CollectionFetchReturn ) rtn;
@@ -202,6 +209,7 @@
 					entityAliases.add( fetchRtn.getElementEntityAliases() );
 					ArrayHelper.addAll( querySpaces, elementPersister.getQuerySpaces() );
 				}
+				includeInResultRowList.add( false );
 			}
 			else {
 				throw new HibernateException( "unexpected custom query return type : " + rtn.getClass().getName() );
@@ -240,6 +248,7 @@
 				hasScalars,
 		        ( ResultColumnProcessor[] ) resultColumnProcessors.toArray( new ResultColumnProcessor[ resultColumnProcessors.size() ] )
 		);
+		this.includeInResultRow = ArrayHelper.toBooleanArray( includeInResultRowList );
 	}
 
 	private Queryable determineAppropriateOwnerPersister(NonScalarReturn ownerDescriptor) {
@@ -332,6 +341,18 @@
 		}
 	}
 	
+	protected String[] getResultRowAliases(){
+		return transformerAliases;
+	}
+	
+	protected ResultTransformer resolveResultTransformer(ResultTransformer resultTransformer) {
+		return HolderInstantiator.resolveResultTransformer( null, resultTransformer );
+	}
+	
+	protected boolean[] includeInResultRow(){
+		return includeInResultRow;
+	}
+	
 	protected Object getResultColumnOrRow(
 			Object[] row,
 	        ResultTransformer transformer,
@@ -340,6 +361,11 @@
 		return rowProcessor.buildResultRow( row, rs, transformer != null, session );
 	}
 
+	protected Object[] getResultRow( Object[] row, ResultSet rs, SessionImplementor session )
+	throws SQLException, HibernateException{
+		return rowProcessor.buildResultRow( row, rs, session );
+	}
+	
 	protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {
 		// meant to handle dynamic instantiation queries...(Copy from QueryLoader)
 		HolderInstantiator holderInstantiator = HolderInstantiator.getHolderInstantiator(
@@ -430,6 +456,17 @@
 				ResultSet resultSet,
 				boolean hasTransformer,
 				SessionImplementor session) throws SQLException, HibernateException {
+			Object[] resultRow = buildResultRow( data, resultSet, session );
+			return ( hasTransformer )
+		       ? resultRow
+		       : ( resultRow.length == 1 )
+		         ? resultRow[0]
+		         : resultRow;
+	}
+	public Object[] buildResultRow(
+			Object[] data,
+			ResultSet resultSet,
+			SessionImplementor session) throws SQLException, HibernateException {
 			Object[] resultRow;
 			if ( !hasScalars ) {
 				resultRow = data;
@@ -444,11 +481,7 @@
 				}
 			}
 
-			return ( hasTransformer )
-			       ? resultRow
-			       : ( resultRow.length == 1 )
-			         ? resultRow[0]
-			         : resultRow;
+			return resultRow;
 		}
 	}
 

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/hql/QueryLoader.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/hql/QueryLoader.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/loader/hql/QueryLoader.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -27,6 +27,7 @@
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -343,33 +344,45 @@
 	private boolean hasSelectNew() {
 		return selectNewTransformer!=null;
 	}
+	
+	protected String[] getResultRowAliases() {
+		return queryReturnAliases;
+	}
+	
+	protected ResultTransformer resolveResultTransformer(ResultTransformer resultTransformer) {
+		return HolderInstantiator.resolveResultTransformer( selectNewTransformer, resultTransformer );
+	}
+	
+	protected boolean[] includeInResultRow() {
+		boolean[] includeInResultTuple = includeInSelect;
+		if ( hasScalars ) {
+			includeInResultTuple = new boolean[ queryReturnTypes.length ];
+			Arrays.fill( includeInResultTuple, true );
+		}
+		return includeInResultTuple;
+	}
 
-	protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session)
+	protected Object getResultColumnOrRow( Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session )
 			throws SQLException, HibernateException {
 
-		row = toResultRow( row );
-		boolean hasTransform = hasSelectNew() || transformer!=null;
+		Object[] resultRow = getResultRow( row, rs, session );
+		boolean hasTransform = hasSelectNew() || transformer != null;
+		return (!hasTransform && resultRow.length == 1 ? resultRow[0] : resultRow);
+	}
+
+	protected Object[] getResultRow( Object[] row, ResultSet rs, SessionImplementor session ) throws SQLException, HibernateException {
+		Object[] resultRow;
 		if ( hasScalars ) {
 			String[][] scalarColumns = scalarColumnNames;
 			int queryCols = queryReturnTypes.length;
-			if ( !hasTransform && queryCols == 1 ) {
-				return queryReturnTypes[0].nullSafeGet( rs, scalarColumns[0], session, null );
+			resultRow = new Object[queryCols];
+			for ( int i = 0; i < queryCols; i++ ) {
+				resultRow[i] = queryReturnTypes[i].nullSafeGet( rs, scalarColumns[i], session, null );
 			}
-			else {
-				row = new Object[queryCols];
-				for ( int i = 0; i < queryCols; i++ ) {
-					row[i] = queryReturnTypes[i].nullSafeGet( rs, scalarColumns[i], session, null );
-				}
-				return row;
-			}
+		} else {
+			resultRow = toResultRow( row );
 		}
-		else if ( !hasTransform ) {
-			return row.length == 1 ? row[0] : row;
-		}
-		else {
-			return row;
-		}
-
+		return resultRow;
 	}
 
 	protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/property/Getter.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/property/Getter.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/property/Getter.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -39,7 +39,7 @@
 public interface Getter extends Serializable {
 	/**
 	 * Get the property value from the given instance .
-	 * @param owner The instance containing the value to be retreived.
+	 * @param owner The instance containing the value to be retrieved.
 	 * @return The extracted value.
 	 * @throws HibernateException
 	 */
@@ -48,7 +48,7 @@
 	/**
 	 * Get the property value from the given owner instance.
 	 *
-	 * @param owner The instance containing the value to be retreived.
+	 * @param owner The instance containing the value to be retrieved.
 	 * @param mergeMap a map of merged persistent instances to detached instances
 	 * @param session The session from which this request originated.
 	 * @return The extracted value.

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/property/PropertyAccessorFactory.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/property/PropertyAccessorFactory.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/property/PropertyAccessorFactory.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -87,7 +87,7 @@
 		    throw new MappingException( "Unknown entity mode [" + mode + "]" );
 	    }
 	}	/**
-	 * Retreives a PropertyAccessor specific for a PojoRepresentation with the given access strategy.
+	 * Retrieves a PropertyAccessor specific for a PojoRepresentation with the given access strategy.
 	 *
 	 * @param pojoAccessorStrategy The access strategy.
 	 * @return An appropriate accessor.

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/AliasToBeanConstructorResultTransformer.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/AliasToBeanConstructorResultTransformer.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/AliasToBeanConstructorResultTransformer.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -41,7 +41,7 @@
 	/**
 	 * Instantiates a AliasToBeanConstructorResultTransformer.
 	 *
-	 * @param constructor The contructor in which to wrap the tuples.
+	 * @param constructor The constructor in which to wrap the tuples.
 	 */
 	public AliasToBeanConstructorResultTransformer(Constructor constructor) {
 		this.constructor = constructor;

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/AliasToBeanResultTransformer.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/AliasToBeanResultTransformer.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/AliasToBeanResultTransformer.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -24,7 +24,7 @@
  */
 package org.hibernate.transform;
 
-import java.util.List;
+import java.util.Arrays;
 
 import org.hibernate.HibernateException;
 import org.hibernate.property.ChainedPropertyAccessor;
@@ -52,42 +52,43 @@
  *
  * @author max
  */
-public class AliasToBeanResultTransformer implements ResultTransformer {
+public class AliasToBeanResultTransformer extends AliasedTupleSubsetResultTransformer {
 
 	// IMPL NOTE : due to the delayed population of setters (setters cached
 	// 		for performance), we really cannot pro0perly define equality for
 	// 		this transformer
 
 	private final Class resultClass;
-	private final PropertyAccessor propertyAccessor;
+	private boolean isInitialized;
+	private String[] aliases;
 	private Setter[] setters;
 
 	public AliasToBeanResultTransformer(Class resultClass) {
 		if ( resultClass == null ) {
 			throw new IllegalArgumentException( "resultClass cannot be null" );
 		}
+		this.isInitialized = false;
 		this.resultClass = resultClass;
-		propertyAccessor = new ChainedPropertyAccessor(
-				new PropertyAccessor[] {
-						PropertyAccessorFactory.getPropertyAccessor( resultClass, null ),
-						PropertyAccessorFactory.getPropertyAccessor( "field" )
-				}
-		);
 	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
+		return false;
+	}
 
 	public Object transformTuple(Object[] tuple, String[] aliases) {
 		Object result;
 
 		try {
-			if ( setters == null ) {
-				setters = new Setter[aliases.length];
-				for ( int i = 0; i < aliases.length; i++ ) {
-					String alias = aliases[i];
-					if ( alias != null ) {
-						setters[i] = propertyAccessor.getSetter( resultClass, alias );
-					}
-				}
+			if ( ! isInitialized ) {
+				initialize( aliases );
 			}
+			else {
+				check( aliases );
+			}
+			
 			result = resultClass.newInstance();
 
 			for ( int i = 0; i < aliases.length; i++ ) {
@@ -106,14 +107,57 @@
 		return result;
 	}
 
-	public List transformList(List collection) {
-		return collection;
+	private void initialize(String[] aliases) {
+		PropertyAccessor propertyAccessor = new ChainedPropertyAccessor(
+				new PropertyAccessor[] {
+						PropertyAccessorFactory.getPropertyAccessor( resultClass, null ),
+						PropertyAccessorFactory.getPropertyAccessor( "field" )
+				}
+		);
+		this.aliases = new String[ aliases.length ];
+		setters = new Setter[ aliases.length ];
+		for ( int i = 0; i < aliases.length; i++ ) {
+			String alias = aliases[ i ];
+			if ( alias != null ) {
+				this.aliases[ i ] = alias;
+				setters[ i ] = propertyAccessor.getSetter( resultClass, alias );
+			}
+		}
+		isInitialized = true;
 	}
+	
+	private void check(String[] aliases) {
+		if ( ! Arrays.equals( aliases, this.aliases ) ) {
+			throw new IllegalStateException(
+					"aliases are different from what is cached; aliases=" + Arrays.asList( aliases ) +
+							" cached=" + Arrays.asList( this.aliases ) );
+		}
+	}
 
+	public boolean equals(Object o) {
+		if ( this == o ) {
+			return true;
+		}
+		if ( o == null || getClass() != o.getClass() ) {
+			return false;
+		}
+
+		AliasToBeanResultTransformer that = ( AliasToBeanResultTransformer ) o;
+
+		if ( ! resultClass.equals( that.resultClass ) ) {
+			return false;
+		}
+		if ( ! Arrays.equals( aliases, that.aliases ) ) {
+			return false;
+		}
+
+		return true;
+	}
+
 	public int hashCode() {
-		int result;
-		result = resultClass.hashCode();
-		result = 31 * result + propertyAccessor.hashCode();
+		int result = resultClass.hashCode();
+		result = 31 * result + ( aliases != null ? Arrays.hashCode( aliases ) : 0 );
 		return result;
 	}
+
 }

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/AliasToEntityMapResultTransformer.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/AliasToEntityMapResultTransformer.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/AliasToEntityMapResultTransformer.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -24,7 +24,6 @@
  */
 package org.hibernate.transform;
 
-import java.io.Serializable;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -39,7 +38,7 @@
  * @author Gavin King
  * @author Steve Ebersole
  */
-public class AliasToEntityMapResultTransformer extends BasicTransformerAdapter implements Serializable {
+public class AliasToEntityMapResultTransformer extends AliasedTupleSubsetResultTransformer {
 
 	public static final AliasToEntityMapResultTransformer INSTANCE = new AliasToEntityMapResultTransformer();
 
@@ -52,7 +51,7 @@
 	 */
 	public AliasToEntityMapResultTransformer() {
 	}
-
+	
 	/**
 	 * {@inheritDoc}
 	 */
@@ -66,7 +65,15 @@
 		}
 		return result;
 	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
+		return false;
+	}
 
+
 	/**
 	 * Serialization hook for ensuring singleton uniqueing.
 	 *

Added: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/AliasedTupleSubsetResultTransformer.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/AliasedTupleSubsetResultTransformer.java	                        (rev 0)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/AliasedTupleSubsetResultTransformer.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -0,0 +1,58 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ *
+ */
+package org.hibernate.transform;
+
+/**
+ * An implementation of TupleSubsetResultTransformer that ignores a
+ * tuple element if its corresponding alias is null.
+ *
+ * @author Gail Badner
+ */
+public abstract class AliasedTupleSubsetResultTransformer
+		extends BasicTransformerAdapter
+		implements TupleSubsetResultTransformer {
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean[] includeInTransform(String[] aliases, int tupleLength) {
+		if ( aliases == null ) {
+			throw new IllegalArgumentException( "aliases cannot be null" );
+		}
+		if ( aliases.length != tupleLength ) {
+			throw new IllegalArgumentException(
+					"aliases and tupleLength must have the same length; " +
+							"aliases.length=" + aliases.length + "tupleLength=" + tupleLength
+			);
+		}
+		boolean[] includeInTransform = new boolean[tupleLength];
+		for ( int i = 0 ; i < aliases.length ; i++ ) {
+			if ( aliases[ i ] != null ) {
+				includeInTransform[ i ] = true;
+			}
+		}
+		return includeInTransform;
+	}
+}
\ No newline at end of file

Added: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/CacheableResultTransformer.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/CacheableResultTransformer.java	                        (rev 0)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/CacheableResultTransformer.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -0,0 +1,347 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ *
+ */
+package org.hibernate.transform;
+
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.List;
+
+import org.hibernate.type.Type;
+import org.hibernate.util.ArrayHelper;
+
+/**
+ * A ResultTransformer that is used to transfor tuples to a value(s)
+ * that can be cached.
+ *
+ * @author Gail Badner
+ */
+public class CacheableResultTransformer implements ResultTransformer {
+
+	// would be nice to be able to have this class extend
+	// PassThroughResultTransformer, but the default constructor
+	// is private (as it should be for a singleton)
+	private final static PassThroughResultTransformer ACTUAL_TRANSFORMER =
+			PassThroughResultTransformer.INSTANCE;
+	private final int tupleLength;
+	private final int tupleSubsetLength;
+
+	// array with the i-th element indicating whether the i-th
+	// expression returned by a query is included in the tuple;
+	// IMPLLEMENTATION NOTE:
+	// "joined" and "fetched" associations may use the same SQL,
+	// but result in different tuple and cached values. This is
+	// because "fetched" associations are excluded from the tuple.
+	//  includeInTuple provides a way to distinguish these 2 cases.
+	private final boolean[] includeInTuple;
+
+	// indexes for tuple that are included in the transformation;
+	// set to null if all elements in the tuple are included
+	private final int[] includeInTransformIndex;
+
+	/**
+	 * Returns a CacheableResultTransformer that is used to transform
+	 * tuples to a value(s) that can be cached.
+	 *
+	 * @param transformer - result transformer that will ultimately be
+	 *        be used (after caching results)
+	 * @param aliases - the aliases that correspond to the tuple;
+	 *        if it is non-null, its length must equal the number
+	 *        of true elements in includeInTuple[]
+	 * @param includeInTuple - array with the i-th element indicating
+	 *        whether the i-th expression returned by a query is
+	 *        included in the tuple; the number of true values equals
+	 *        the length of the tuple that will be transformed;
+	 *        must be non-null
+	 * @return a CacheableResultTransformer that is used to transform
+	 *         tuples to a value(s) that can be cached.
+	 */
+	public static CacheableResultTransformer create(ResultTransformer transformer,
+													String[] aliases,
+													boolean[] includeInTuple) {
+		return transformer instanceof TupleSubsetResultTransformer ?
+				create( ( TupleSubsetResultTransformer ) transformer, aliases, includeInTuple ) :
+				create( includeInTuple )
+		;
+	}
+
+	/**
+	 * Returns a CacheableResultTransformer that is used to transform
+	 * tuples to a value(s) that can be cached.
+	 *
+	 * @param transformer - a tuple subset result transformer;
+	 *        must be non-null;
+	 * @param aliases - the aliases that correspond to the tuple;
+	 *        if it is non-null, its length must equal the number
+	 *        of true elements in includeInTuple[]
+	 * @param includeInTuple - array with the i-th element indicating
+	 *        whether the i-th expression returned by a query is
+	 *        included in the tuple; the number of true values equals
+	 *        the length of the tuple that will be transformed;
+	 *        must be non-null
+	 * @return a CacheableResultTransformer that is used to transform
+	 *         tuples to a value(s) that can be cached.
+	 */
+	private static CacheableResultTransformer create(TupleSubsetResultTransformer transformer,
+													 String[] aliases,
+													 boolean[] includeInTuple) {
+		if ( transformer == null ) {
+			throw new IllegalArgumentException( "transformer cannot be null" );
+		}
+		int tupleLength = ArrayHelper.countTrue( includeInTuple );
+		if ( aliases != null && aliases.length != tupleLength ) {
+			throw new IllegalArgumentException(
+					"if aliases is not null, then the length of aliases[] must equal the number of true elements in includeInTuple; " +
+							"aliases.length=" + aliases.length + "tupleLength=" + tupleLength
+			);
+		}
+		return new CacheableResultTransformer(
+				includeInTuple,
+				transformer.includeInTransform( aliases, tupleLength )
+		);
+	}
+
+	/**
+	 * Returns a CacheableResultTransformer that is used to transform
+	 * tuples to a value(s) that can be cached.
+	 *
+	 * @param includeInTuple - array with the i-th element indicating
+	 *        whether the i-th expression returned by a query is
+	 *        included in the tuple; the number of true values equals
+	 *        the length of the tuple that will be transformed;
+	 *        must be non-null
+	 * @return a CacheableResultTransformer that is used to transform
+	 *         tuples to a value(s) that can be cached.
+	 */
+	private static CacheableResultTransformer create(boolean[] includeInTuple) {
+		return new CacheableResultTransformer( includeInTuple, null );
+	}
+
+	private CacheableResultTransformer(boolean[] includeInTuple, boolean[] includeInTransform) {
+		if ( includeInTuple == null ) {
+			throw new IllegalArgumentException( "includeInTuple cannot be null" );
+		}
+		this.includeInTuple = includeInTuple;
+		tupleLength = ArrayHelper.countTrue( includeInTuple );
+		tupleSubsetLength = (
+				includeInTransform == null ?
+						tupleLength :
+						ArrayHelper.countTrue( includeInTransform )
+		);
+		if ( tupleSubsetLength == tupleLength ) {
+			includeInTransformIndex = null;
+		}
+		else {
+			includeInTransformIndex = new int[tupleSubsetLength];
+			for ( int i = 0, j = 0 ; i < includeInTransform.length ; i++ ) {
+				if ( includeInTransform[ i ] ) {
+					includeInTransformIndex[ j ] =  i;
+					j++;
+				}
+			}
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Object transformTuple(Object[] tuple, String aliases[]) {
+		if ( aliases != null && aliases.length != tupleLength ) {
+			throw new IllegalStateException(
+					"aliases expected length is " + tupleLength +
+					"; actual length is " + aliases.length );
+		}
+		// really more correct to pass index( aliases.getClass(), aliases )
+		// as the 2nd arg to the following statement;
+		// passing null instead because it ends up being ignored.
+		return ACTUAL_TRANSFORMER.transformTuple( index( tuple.getClass(), tuple ), null );
+	}
+
+	/**
+	 * Re-transforms, if necessary, a List of values previously
+	 * transformed by this (or an equivalent) CacheableResultTransformer.
+	 * Each element of the list is re-transformed in place (i.e, List
+	 * elements are replaced with re-transformed values) and the original
+	 * List is returned.
+	 * <p/>
+	 * If re-transformation is unnecessary, the original List is returned
+	 * unchanged.
+	 *
+	 * @param transformedResults - results that were previously transformed
+	 * @param aliases - the aliases that correspond to the untransformed tuple;
+	 * @param transformer - the transformer for the re-transformation
+	 * @return transformedResults, with each element re-transformed (if nececessary)
+	 */
+	public List retransformResults(List transformedResults,
+								   String aliases[],
+								   ResultTransformer transformer,
+								   boolean[] includeInTuple) {
+		if ( transformer == null ) {
+			throw new IllegalArgumentException( "transformer cannot be null" );
+		}
+		if ( ! this.equals( create( transformer, aliases, includeInTuple ) ) ) {
+			throw new IllegalStateException(
+					"this CacheableResultTransformer is inconsistent with specified arguments; cannot re-transform"
+			);
+		}
+		boolean requiresRetransform = true;
+		String[] aliasesToUse = aliases == null ? null : index( ( aliases.getClass() ), aliases );
+		if ( transformer == ACTUAL_TRANSFORMER ) {
+			requiresRetransform = false;
+		}
+		else if ( transformer instanceof TupleSubsetResultTransformer ) {
+			requiresRetransform =  ! ( ( TupleSubsetResultTransformer ) transformer ).isTransformedValueATupleElement(
+					aliasesToUse,
+					tupleLength
+			);
+		}
+		if ( requiresRetransform ) {
+			for ( int i = 0 ; i < transformedResults.size() ; i++ ) {
+				Object[] tuple = ACTUAL_TRANSFORMER.untransformToTuple(
+									transformedResults.get( i ),
+									tupleSubsetLength == 1
+				);
+				transformedResults.set( i, transformer.transformTuple( tuple, aliasesToUse ) );
+			}
+		}
+		return transformedResults;
+	}
+
+	/**
+	 * Untransforms, if necessary, a List of values previously
+	 * transformed by this (or an equivalent) CacheableResultTransformer.
+	 * Each element of the list is untransformed in place (i.e, List
+	 * elements are replaced with untransformed values) and the original
+	 * List is returned.
+	 * <p/>
+	 * If not unnecessary, the original List is returned
+	 * unchanged.
+	 * <p/>
+	 * NOTE: If transformed values are a subset of the original
+	 *       tuple, then, on return, elements corresponding to
+	 *       excluded tuple elements will be null.
+	 * @param results - results that were previously transformed
+	 * @return results, with each element untransformed (if nececessary)
+	 */
+	public List untransformToTuples(List results) {
+		if ( includeInTransformIndex == null ) {
+			results = ACTUAL_TRANSFORMER.untransformToTuples(
+					results,
+					tupleSubsetLength == 1
+			);
+		}
+		else {
+			for ( int i = 0 ; i < results.size() ; i++ ) {
+				Object[] tuple = ACTUAL_TRANSFORMER.untransformToTuple(
+									results.get( i ),
+									tupleSubsetLength == 1
+				);
+				results.set( i, unindex( tuple.getClass(), tuple ) );
+			}
+
+		}
+		return results;
+	}
+
+	/**
+	 * Returns the result types for the transformed value.
+	 * @param tupleResultTypes
+	 * @return
+	 */
+	public Type[] getCachedResultTypes(Type[] tupleResultTypes) {
+		return tupleLength != tupleSubsetLength ?
+				index( tupleResultTypes.getClass(), tupleResultTypes ) :
+				tupleResultTypes
+		;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public List transformList(List list) {
+		return list;
+	}
+
+	private <T> T[] index(Class<? extends T[]> clazz, T[] objects) {
+		T[] objectsIndexed = objects;
+		if ( objects != null &&
+				includeInTransformIndex != null &&
+				objects.length != tupleSubsetLength ) {
+			objectsIndexed = clazz.cast( Array.newInstance( clazz.getComponentType(), tupleSubsetLength ) );
+			for ( int i = 0 ; i < tupleSubsetLength; i++ ) {
+				objectsIndexed[ i ] = objects[ includeInTransformIndex[ i ] ];
+			}
+		}
+		return objectsIndexed;
+	}
+
+	private <T> T[] unindex(Class<? extends T[]> clazz, T[] objects) {
+		T[] objectsUnindexed = objects;
+		if ( objects != null &&
+				includeInTransformIndex != null &&
+				objects.length != tupleLength ) {
+			objectsUnindexed = clazz.cast( Array.newInstance( clazz.getComponentType(), tupleLength ) );
+			for ( int i = 0 ; i < tupleSubsetLength; i++ ) {
+				objectsUnindexed[ includeInTransformIndex[ i ] ] = objects[ i ];
+			}
+		}
+		return objectsUnindexed;
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if ( this == o ) {
+			return true;
+		}
+		if ( o == null || getClass() != o.getClass() ) {
+			return false;
+		}
+
+		CacheableResultTransformer that = ( CacheableResultTransformer ) o;
+
+		if ( tupleLength != that.tupleLength ) {
+			return false;
+		}
+		if ( tupleSubsetLength != that.tupleSubsetLength ) {
+			return false;
+		}
+		if ( !Arrays.equals( includeInTuple, that.includeInTuple ) ) {
+			return false;
+		}
+		if ( !Arrays.equals( includeInTransformIndex, that.includeInTransformIndex ) ) {
+			return false;
+		}
+
+		return true;
+	}
+
+	@Override
+	public int hashCode() {
+		int result = tupleLength;
+		result = 31 * result + tupleSubsetLength;
+		result = 31 * result + ( includeInTuple != null ? Arrays.hashCode( includeInTuple ) : 0 );
+		result = 31 * result + ( includeInTransformIndex != null ? Arrays.hashCode( includeInTransformIndex ) : 0 );
+		return result;
+	}
+}

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/DistinctResultTransformer.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/DistinctResultTransformer.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/DistinctResultTransformer.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -24,7 +24,6 @@
  */
 package org.hibernate.transform;
 
-import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
@@ -42,7 +41,7 @@
  *
  * @author Steve Ebersole
  */
-public class DistinctResultTransformer extends BasicTransformerAdapter implements Serializable {
+public class DistinctResultTransformer extends BasicTransformerAdapter {
 
 	public static final DistinctResultTransformer INSTANCE = new DistinctResultTransformer();
 

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/DistinctRootEntityResultTransformer.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/DistinctRootEntityResultTransformer.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/DistinctRootEntityResultTransformer.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -37,7 +37,7 @@
  * @author Gavin King
  * @author Steve Ebersole
  */
-public class DistinctRootEntityResultTransformer implements ResultTransformer, Serializable {
+public class DistinctRootEntityResultTransformer implements TupleSubsetResultTransformer {
 
 	public static final DistinctRootEntityResultTransformer INSTANCE = new DistinctRootEntityResultTransformer();
 
@@ -71,6 +71,20 @@
 	public List transformList(List list) {
 		return DistinctResultTransformer.INSTANCE.transformList( list );
 	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean[] includeInTransform(String[] aliases, int tupleLength) {
+		return RootEntityResultTransformer.INSTANCE.includeInTransform( aliases, tupleLength );
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength){
+		return RootEntityResultTransformer.INSTANCE.isTransformedValueATupleElement( null, tupleLength );
+	}
 
 	/**
 	 * Serialization hook for ensuring singleton uniqueing.

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/PassThroughResultTransformer.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/PassThroughResultTransformer.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/PassThroughResultTransformer.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -24,14 +24,15 @@
  */
 package org.hibernate.transform;
 
-import java.io.Serializable;
+import java.util.Arrays;
+import java.util.List;
 
 /**
  * ???
  *
  * @author max
  */
-public class PassThroughResultTransformer extends BasicTransformerAdapter implements Serializable {
+public class PassThroughResultTransformer extends BasicTransformerAdapter implements TupleSubsetResultTransformer {
 
 	public static final PassThroughResultTransformer INSTANCE = new PassThroughResultTransformer();
 
@@ -46,6 +47,23 @@
 	public Object transformTuple(Object[] tuple, String[] aliases) {
 		return tuple.length==1 ? tuple[0] : tuple;
 	}
+	
+	/* package-protected */
+	List untransformToTuples(List results, boolean isSingleResult) {
+		// untransform only if necessary; if transformed, do it in place;
+		if ( isSingleResult ) {
+			for ( int i = 0 ; i < results.size() ; i++ ) {
+				Object[] tuple = untransformToTuple( results.get( i ), isSingleResult);
+				results.set( i, tuple );
+			}
+		}
+		return results;
+	}
+	
+	/* package-protected */
+	Object[] untransformToTuple(Object transformed, boolean isSingleResult ) {
+		return isSingleResult ? new Object[] { transformed } : ( Object[] ) transformed;
+	}
 
 	/**
 	 * Serialization hook for ensuring singleton uniqueing.
@@ -56,14 +74,20 @@
 		return INSTANCE;
 	}
 
-	public int hashCode() {
-		// todo : we can remove this once the deprecated ctor can be made private...
-		return PassThroughResultTransformer.class.getName().hashCode();
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
+		return tupleLength == 1;
 	}
 
-	public boolean equals(Object other) {
-		// todo : we can remove this once the deprecated ctor can be made private...
-		return other != null && PassThroughResultTransformer.class.isInstance( other );
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean[] includeInTransform(String[] aliases, int tupleLength) {
+		boolean[] includeInTransformedResult = new boolean[tupleLength];
+		Arrays.fill( includeInTransformedResult, true );
+		return includeInTransformedResult;
 	}
 
 }

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/RootEntityResultTransformer.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/RootEntityResultTransformer.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/RootEntityResultTransformer.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -24,8 +24,9 @@
  */
 package org.hibernate.transform;
 
-import java.io.Serializable;
 
+import org.hibernate.util.ArrayHelper;
+
 /**
  * {@link ResultTransformer} implementation which limits the result tuple
  * to only the "root entity".
@@ -36,7 +37,7 @@
  * @author Gavin King
  * @author Steve Ebersole
  */
-public final class RootEntityResultTransformer extends BasicTransformerAdapter implements Serializable {
+public final class RootEntityResultTransformer extends BasicTransformerAdapter implements TupleSubsetResultTransformer {
 
 	public static final RootEntityResultTransformer INSTANCE = new RootEntityResultTransformer();
 
@@ -54,8 +55,29 @@
 	public Object transformTuple(Object[] tuple, String[] aliases) {
 		return tuple[ tuple.length-1 ];
 	}
-
+	
 	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
+		return true;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean[] includeInTransform(String[] aliases, int tupleLength) {
+		boolean[] includeInTransform;
+		if( tupleLength==1 ){
+			includeInTransform = ArrayHelper.TRUE;
+		}
+		else {
+			includeInTransform = new boolean[tupleLength];
+			includeInTransform[tupleLength-1]=true;
+		}
+		return includeInTransform;
+	}
+	/**
 	 * Serialization hook for ensuring singleton uniqueing.
 	 *
 	 * @return The singleton instance : {@link #INSTANCE}

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/ToListResultTransformer.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/ToListResultTransformer.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/ToListResultTransformer.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -24,7 +24,6 @@
  */
 package org.hibernate.transform;
 
-import java.io.Serializable;
 import java.util.Arrays;
 import java.util.List;
 
@@ -32,7 +31,7 @@
  * Tranforms each result row from a tuple into a {@link List}, such that what
  * you end up with is a {@link List} of {@link List Lists}.
  */
-public class ToListResultTransformer extends BasicTransformerAdapter implements Serializable {
+public class ToListResultTransformer extends BasicTransformerAdapter {
 
 	public static final ToListResultTransformer INSTANCE = new ToListResultTransformer();
 

Added: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/TupleSubsetResultTransformer.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/TupleSubsetResultTransformer.java	                        (rev 0)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/transform/TupleSubsetResultTransformer.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -0,0 +1,84 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ *
+ */
+package org.hibernate.transform;
+
+/**
+ * A ResultTransformer that operates on "well-defined" and consistent
+ * subset of a tuple's elements.
+ *
+ * "Well-defined" means that:
+ * <ol>
+ *     <li>
+ *         the indexes of tuple elements accessed by a
+ *         TupleSubsetResultTransformer depends only on the aliases
+ *         and the number of elements in the tuple; i.e, it does
+ *         not depend on the value of the tuple being transformed;
+ *     </li>
+ *     <li>
+ *         any tuple elements included in the transformed value are
+ *         unmodified by the transformation;
+ *     </li>
+ *     <li>
+ *         transforming equivalent tuples with the same aliases multiple
+ *         times results in transformed values that are equivalent;
+ *     </li>
+ *     <li>
+ *         the result of transforming the tuple subset (only those
+ *         elements accessed by the transformer) using only the
+ *         corresponding aliases is equivalent to transforming the
+ *         full tuple with the full array of aliases;
+ *     </li>
+ *     <li>
+ *         the result of transforming a tuple with non-accessed tuple
+ *         elements and corresponding aliases set to null
+ *         is equivalent to transforming the full tuple with the
+ *         full array of aliases;
+ *     </li>
+ * </ol>
+ *
+ * @author Gail Badner
+ */
+public interface TupleSubsetResultTransformer extends ResultTransformer {
+	/**
+	 * When a tuple is transformed, is the result a single element of the tuple?
+	 *
+	 * @param aliases - the aliases that correspond to the tuple
+	 * @param tupleLength - the number of elements in the tuple
+	 * @return true, if the transformed value is a single element of the tuple;
+	 *         false, otherwise.
+	 */
+	boolean isTransformedValueATupleElement(String[] aliases, int tupleLength);
+
+	/**
+	 * Returns an array with the i-th element indicating whether the i-th
+	 * element of the tuple is included in the transformed value.
+	 *
+	 * @param aliases - the aliases that correspond to the tuple
+	 * @param tupleLength - the number of elements in the tuple
+	 * @return array with the i-th element indicating whether the i-th
+	 *         element of the tuple is included in the transformed value.
+	 */
+	boolean[] includeInTransform(String[] aliases, int tupleLength);
+}
\ No newline at end of file

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/usertype/CompositeUserType.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/usertype/CompositeUserType.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/usertype/CompositeUserType.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -169,7 +169,7 @@
 	 *
 	 * @param value the object to be cached
 	 * @param session
-	 * @return a cachable representation of the object
+	 * @return a cacheable representation of the object
 	 * @throws HibernateException
 	 */
 	public Serializable disassemble(Object value, SessionImplementor session) throws HibernateException;
@@ -181,7 +181,7 @@
 	 * @param cached the object to be cached
 	 * @param session
 	 * @param owner the owner of the cached object
-	 * @return a reconstructed object from the cachable representation
+	 * @return a reconstructed object from the cacheable representation
 	 * @throws HibernateException
 	 */
 	public Object assemble(Serializable cached, SessionImplementor session, Object owner) 

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/usertype/EnhancedUserType.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/usertype/EnhancedUserType.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/usertype/EnhancedUserType.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -26,7 +26,7 @@
 
 /**
  * A custom type that may function as an identifier or
- * discriminator type, or may be marshalled to and from
+ * discriminator type, or may be marshaled to and from
  * an XML document
  * 
  * @author Gavin King

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/usertype/LoggableUserType.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/usertype/LoggableUserType.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/usertype/LoggableUserType.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -36,7 +36,7 @@
 	/**
 	 * Generate a loggable string representation of the collection (value).
 	 *
-	 * @param value The collection to be logged; guarenteed to be non-null and initialized.
+	 * @param value The collection to be logged; guaranteed to be non-null and initialized.
 	 * @param factory The factory.
 	 * @return The loggable string representation.
 	 */

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/usertype/UserCollectionType.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/usertype/UserCollectionType.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/usertype/UserCollectionType.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -83,7 +83,7 @@
 	 * but with the given anticipated size (i.e. accounting for initial size
 	 * and perhaps load factor).
 	 *
-	 * @param anticipatedSize The anticipated size of the instaniated collection
+	 * @param anticipatedSize The anticipated size of the instantiated collection
 	 * after we are done populating it.  Note, may be negative to indicate that
 	 * we not yet know anything about the anticipated size (i.e., when initializing
 	 * from a result set row by row).

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/usertype/UserType.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/usertype/UserType.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/main/java/org/hibernate/usertype/UserType.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -146,7 +146,7 @@
 	 * identifier values. (optional operation)
 	 *
 	 * @param value the object to be cached
-	 * @return a cachable representation of the object
+	 * @return a cacheable representation of the object
 	 * @throws HibernateException
 	 */
 	public Serializable disassemble(Object value) throws HibernateException;
@@ -157,7 +157,7 @@
 	 *
 	 * @param cached the object to be cached
 	 * @param owner the owner of the cached object
-	 * @return a reconstructed object from the cachable representation
+	 * @return a reconstructed object from the cacheable representation
 	 * @throws HibernateException
 	 */
 	public Object assemble(Serializable cached, Object owner) throws HibernateException;

Modified: core/branches/Branch_3_3_2_GA_CP/core/src/test/java/org/hibernate/cache/QueryKeyTest.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/core/src/test/java/org/hibernate/cache/QueryKeyTest.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/core/src/test/java/org/hibernate/cache/QueryKeyTest.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -20,21 +20,29 @@
  * Free Software Foundation, Inc.
  * 51 Franklin Street, Fifth Floor
  * Boston, MA  02110-1301  USA
+ *
  */
 package org.hibernate.cache;
 
+import java.io.Serializable;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.Map;
 
 import junit.framework.TestCase;
 
 import org.hibernate.EntityMode;
+import org.hibernate.transform.AliasToBeanResultTransformer;
 import org.hibernate.transform.AliasToEntityMapResultTransformer;
+import org.hibernate.transform.AliasedTupleSubsetResultTransformer;
+import org.hibernate.transform.CacheableResultTransformer;
 import org.hibernate.transform.DistinctResultTransformer;
 import org.hibernate.transform.DistinctRootEntityResultTransformer;
 import org.hibernate.transform.PassThroughResultTransformer;
 import org.hibernate.transform.ResultTransformer;
 import org.hibernate.transform.RootEntityResultTransformer;
+import org.hibernate.transform.ToListResultTransformer;
+import org.hibernate.transform.TupleSubsetResultTransformer;
 import org.hibernate.util.ArrayHelper;
 import org.hibernate.util.SerializationHelper;
 
@@ -46,19 +54,176 @@
 public class QueryKeyTest extends TestCase {
 	private static final String QUERY_STRING = "the query string";
 
-	public void testSerializedEquality() {
+	public static class AClass implements Serializable {
+		private String propAccessedByField;
+		private String propAccessedByMethod;
+		private int propValue;
+
+		public AClass() {
+		}
+
+		public AClass(String propAccessedByField) {
+			this.propAccessedByField = propAccessedByField;
+		}
+
+		public String getPropAccessedByMethod() {
+			return propAccessedByMethod;
+		}
+
+		public void setPropAccessedByMethod(String propAccessedByMethod) {
+			this.propAccessedByMethod = propAccessedByMethod;
+		}
+	}
+
+	public void testSerializedEqualityResultTransformer() throws Exception {
+		// settings are lazily initialized when calling transformTuple(),
+		// so they have not been initialized for the following test
+		// (it *should* be initialized before creating a QueryKey)
+		doResultTransformerTest( new AliasToBeanResultTransformer( AClass.class ), false );
+
+		// initialize settings for the next test
+		AliasToBeanResultTransformer transformer = new AliasToBeanResultTransformer( AClass.class );
+		transformer.transformTuple(
+				new Object[] { "abc", "def" },
+				new String[] { "propAccessedByField", "propAccessedByMethod" }
+		);
+		doResultTransformerTest( transformer, false );
+
+		doResultTransformerTest( AliasToEntityMapResultTransformer.INSTANCE, true );
+		doResultTransformerTest( DistinctResultTransformer.INSTANCE, true );
+		doResultTransformerTest( DistinctRootEntityResultTransformer.INSTANCE, true );
+		doResultTransformerTest( PassThroughResultTransformer.INSTANCE, true );
+		doResultTransformerTest( RootEntityResultTransformer.INSTANCE, true );
+		doResultTransformerTest( ToListResultTransformer.INSTANCE, true );
+	}
+
+	// Reproduces HHH-5628; commented out because FailureExpected is not working here...
+	/*
+	public void testAliasToBeanConstructorFailureExpected() throws Exception {
+		// AliasToBeanConstructorResultTransformer is not Serializable because
+		// java.lang.reflect.Constructor is not Serializable;
+		doResultTransformerTest(
+				new AliasToBeanConstructorResultTransformer( AClass.class.getConstructor( String.class ) ), false
+		);
+	}
+	*/
+
+	private void doResultTransformerTest(ResultTransformer transformer, boolean isSingleton) {
+		Map transformerMap = new HashMap();
+
+		transformerMap.put( transformer, "" );
+		assert transformerMap.size() == 1 : "really messed up";
+		Object old = transformerMap.put( transformer, "value" );
+		assert old != null && transformerMap.size() == 1 : "apparent QueryKey equals/hashCode issue";
+
+		// finally, lets serialize it and see what happens
+		ResultTransformer transformer2 = ( ResultTransformer ) SerializationHelper.clone( transformer );
+		old = transformerMap.put( transformer2, "new value" );
+		assert old != null && transformerMap.size() == 1 : "deserialization did not set hashCode or equals properly";
+		if ( isSingleton ) {
+			assert transformer == transformer2: "deserialization issue for singleton transformer";			
+		}
+		else {
+			assert transformer != transformer2: "deserialization issue for non-singleton transformer";
+		}
+		assert transformer.equals( transformer2 ): "deep copy issue";
+	}
+
+	public void testSerializedEquality() throws Exception {
 		doTest( buildBasicKey( null ) );
+
+		doTest( buildBasicKey( CacheableResultTransformer.create( null, null, new boolean[] { true } ) ) );
+		doTest( buildBasicKey( CacheableResultTransformer.create( null, new String[] { null }, new boolean[] { true } ) ) );
+		doTest( buildBasicKey( CacheableResultTransformer.create( null, new String[] { "a" }, new boolean[] { true } ) ) );
+		doTest( buildBasicKey( CacheableResultTransformer.create( null, null, new boolean[] { false, true } ) ) );
+		doTest( buildBasicKey( CacheableResultTransformer.create( null, new String[] { "a" }, new boolean[] { true, false } ) ) );
+		doTest( buildBasicKey( CacheableResultTransformer.create( null, new String[] { "a", null }, new boolean[] { true, true } ) ) );
 	}
 
-	public void testSerializedEqualityWithResultTransformer() {
-		doTest( buildBasicKey( RootEntityResultTransformer.INSTANCE ) );
-		doTest( buildBasicKey( DistinctRootEntityResultTransformer.INSTANCE ) );
-		doTest( buildBasicKey( DistinctResultTransformer.INSTANCE ) );
-		doTest( buildBasicKey( AliasToEntityMapResultTransformer.INSTANCE ) );
-		doTest( buildBasicKey( PassThroughResultTransformer.INSTANCE ) );
+	public void testSerializedEqualityWithTupleSubsetResultTransfprmer() throws Exception {
+		doTestWithTupleSubsetResultTransformer(
+				new AliasToBeanResultTransformer( AClass.class ),
+				new String[] { "propAccessedByField", "propAccessedByMethod" }
+		);
+		doTestWithTupleSubsetResultTransformer( AliasToEntityMapResultTransformer.INSTANCE, new String[] { "a", "b" } );
+		doTestWithTupleSubsetResultTransformer( DistinctRootEntityResultTransformer.INSTANCE, new String[] { "a", "b" } );
+		doTestWithTupleSubsetResultTransformer( PassThroughResultTransformer.INSTANCE, new String[] { "a", "b" } );
+		doTestWithTupleSubsetResultTransformer( RootEntityResultTransformer.INSTANCE, new String[] { "a", "b" } );
+		// The following are not TupleSubsetResultTransformers:
+		// DistinctResultTransformer.INSTANCE
+		// ToListResultTransformer.INSTANCE
 	}
 
-	private QueryKey buildBasicKey(ResultTransformer resultTransformer) {
+	public void doTestWithTupleSubsetResultTransformer(TupleSubsetResultTransformer transformer,
+													   String[] aliases) throws Exception {
+		doTest( buildBasicKey(
+				CacheableResultTransformer.create(
+						transformer,
+						new String[] { aliases[ 0 ], aliases[ 1 ] },
+						new boolean[] { true, true } )
+		) );
+		doTest( buildBasicKey(
+				CacheableResultTransformer.create(
+						transformer,
+						new String[] { aliases[ 0 ], aliases[ 1 ] },
+						new boolean[] { true, true, false } ) 
+		) );
+		doTest( buildBasicKey(
+				CacheableResultTransformer.create(
+						transformer,
+						new String[] { aliases[ 1 ] },
+						new boolean[] { true } )
+		) );
+		doTest( buildBasicKey(
+				CacheableResultTransformer.create(
+						transformer,
+						new String[] { null, aliases[ 1 ] },
+						new boolean[] { true, true } )
+		) );
+		doTest( buildBasicKey(
+				CacheableResultTransformer.create(
+						transformer,
+						new String[] { aliases[ 0 ], null },
+						new boolean[] { true, true } )
+		) );
+		doTest( buildBasicKey(
+				CacheableResultTransformer.create(
+						transformer,
+						new String[] { aliases[ 0 ] },
+						new boolean[] { false, true } )
+		) );
+		doTest( buildBasicKey(
+				CacheableResultTransformer.create(
+						transformer,
+						new String[] { aliases[ 0 ] },
+						new boolean[] { true, false } )
+		) );
+		doTest( buildBasicKey(
+				CacheableResultTransformer.create(
+						transformer,
+						new String[] { aliases[ 0 ] },
+						new boolean[] { false, true, false } )
+		) );
+		if ( ! ( transformer instanceof AliasedTupleSubsetResultTransformer ) ) {
+			doTestWithTupleSubsetResultTransformerNullAliases( transformer );
+		}
+	}
+
+	public void doTestWithTupleSubsetResultTransformerNullAliases(TupleSubsetResultTransformer transformer) throws Exception {
+		doTest( buildBasicKey( CacheableResultTransformer.create( transformer, null, new boolean[] { true } ) ) );
+		doTest( buildBasicKey( CacheableResultTransformer.create( transformer, null, new boolean[] { true, true } ) ) );
+		doTest( buildBasicKey( CacheableResultTransformer.create( transformer, null, new boolean[] { true, true, true } ) ) );
+		doTest( buildBasicKey( CacheableResultTransformer.create( transformer, null, new boolean[] { false, true } ) ) );
+		doTest( buildBasicKey( CacheableResultTransformer.create( transformer, null, new boolean[] { true, false } ) ) );
+		doTest( buildBasicKey( CacheableResultTransformer.create( transformer, null, new boolean[] { false, true, true } ) ) );
+		doTest( buildBasicKey( CacheableResultTransformer.create( transformer, null, new boolean[] {true, false, true } ) ) );
+		doTest( buildBasicKey( CacheableResultTransformer.create( transformer, null, new boolean[] {true, true, false } ) ) );
+		doTest( buildBasicKey( CacheableResultTransformer.create( transformer, null, new boolean[] {false, false, true } ) ) );
+		doTest( buildBasicKey( CacheableResultTransformer.create( transformer, null, new boolean[] {false, true, false } ) ) );
+		doTest( buildBasicKey( CacheableResultTransformer.create( transformer, null, new boolean[] {false, false, true } ) ) );
+	}
+
+	private QueryKey buildBasicKey(CacheableResultTransformer resultTransformer) {
 		return new QueryKey(
 				QUERY_STRING,
 				ArrayHelper.EMPTY_TYPE_ARRAY, 		// positional param types
@@ -73,18 +238,34 @@
 	}
 
 	private void doTest(QueryKey key) {
-		HashMap map = new HashMap();
+		Map keyMap = new HashMap();
+		Map transformerMap = new HashMap();
 
-		map.put( key, "" );
-		assert map.size() == 1 : "really messed up";
+		keyMap.put( key, "" );
+		assert keyMap.size() == 1 : "really messed up";
+		Object old = keyMap.put( key, "value" );
+		assert old != null && keyMap.size() == 1 : "apparent QueryKey equals/hashCode issue";
 
-		Object old = map.put( key, "value" );
-		assert old != null && map.size() == 1 : "apparent QueryKey equals/hashCode issue";
+		if ( key.getResultTransformer() != null ) {
+			transformerMap.put( key.getResultTransformer(), "" );
+			assert transformerMap.size() == 1 : "really messed up";
+			old = transformerMap.put( key.getResultTransformer(), "value" );
+			assert old != null && transformerMap.size() == 1 : "apparent QueryKey equals/hashCode issue";
+		}
 
 		// finally, lets serialize it and see what happens
 		QueryKey key2 = ( QueryKey ) SerializationHelper.clone( key );
 		assert key != key2 : "deep copy issue";
-		old = map.put( key2, "new value" );
-		assert old != null && map.size() == 1 : "deserialization did not set hashCode or equals properly";
+		old = keyMap.put( key2, "new value" );
+		assert old != null && keyMap.size() == 1 : "deserialization did not set hashCode or equals properly";
+		if ( key.getResultTransformer() == null ) {
+			assert key2.getResultTransformer() == null;
+		}
+		else {
+			old = transformerMap.put( key2.getResultTransformer(), "new value" );
+			assert old != null && transformerMap.size() == 1 : "deserialization did not set hashCode or equals properly";
+				assert key.getResultTransformer() != key2.getResultTransformer(): "deserialization issue for non-singleton transformer";
+				assert key.getResultTransformer().equals( key2.getResultTransformer() ): "deep copy issue";
+		}
 	}
 }

Added: core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/AbstractQueryCacheResultTransformerTest.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/AbstractQueryCacheResultTransformerTest.java	                        (rev 0)
+++ core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/AbstractQueryCacheResultTransformerTest.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -0,0 +1,2561 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ *
+ */
+package org.hibernate.test.querycache;
+
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.hibernate.CacheMode;
+import org.hibernate.Criteria;
+import org.hibernate.FetchMode;
+import org.hibernate.Hibernate;
+import org.hibernate.Query;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.criterion.Order;
+import org.hibernate.criterion.Projections;
+import org.hibernate.criterion.Property;
+import org.hibernate.criterion.Restrictions;
+import org.hibernate.impl.SessionFactoryImpl;
+import org.hibernate.junit.functional.FunctionalTestCase;
+import org.hibernate.proxy.HibernateProxy;
+import org.hibernate.transform.AliasToBeanConstructorResultTransformer;
+import org.hibernate.transform.ResultTransformer;
+import org.hibernate.transform.Transformers;
+import org.hibernate.type.Type;
+import org.hibernate.util.ReflectHelper;
+
+/**
+ * @author Gail Badner
+ */
+public abstract class AbstractQueryCacheResultTransformerTest extends FunctionalTestCase {
+
+	private Student yogiExpected;
+	private Student shermanExpected;
+	private CourseMeeting courseMeetingExpected1;
+	private CourseMeeting courseMeetingExpected2;
+	private Course courseExpected;
+	private Enrolment yogiEnrolmentExpected;
+	private Enrolment shermanEnrolmentExpected;
+
+	public AbstractQueryCacheResultTransformerTest(String str) {
+		super( str );
+	}
+
+	public String[] getMappings() {
+		return new String[] { "querycache/Enrolment.hbm.xml" };
+	}
+
+	public void configure(Configuration cfg) {
+		super.configure( cfg );
+		cfg.setProperty( Environment.USE_QUERY_CACHE, "true" );
+		cfg.setProperty( Environment.CACHE_REGION_PREFIX, "foo" );
+		cfg.setProperty( Environment.USE_SECOND_LEVEL_CACHE, "true" );
+		cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
+	}
+
+	protected abstract class CriteriaExecutor extends QueryExecutor {
+		protected abstract Criteria getCriteria(Session s) throws Exception;
+		protected Object getResults(Session s, boolean isSingleResult) throws Exception {
+			Criteria criteria = getCriteria( s ).setCacheable( getQueryCacheMode() != CacheMode.IGNORE ).setCacheMode( getQueryCacheMode() );
+			return ( isSingleResult ? criteria.uniqueResult() : criteria.list() );
+		}
+	}
+
+	protected abstract class HqlExecutor extends QueryExecutor {
+		protected abstract Query getQuery(Session s);
+		protected Object getResults(Session s, boolean isSingleResult) {
+			Query query = getQuery( s ).setCacheable( getQueryCacheMode() != CacheMode.IGNORE ).setCacheMode( getQueryCacheMode() );
+			return ( isSingleResult ? query.uniqueResult() : query.list() );
+		}
+	}
+
+	protected abstract class QueryExecutor {
+		public Object execute(boolean isSingleResult) throws Exception{
+			Session s = openSession();
+			Transaction t = s.beginTransaction();
+			Object result = null;
+			try {
+				result = getResults( s, isSingleResult );
+				t.commit();
+			}
+			catch ( Exception ex ) {
+				t.rollback();
+				throw ex;
+			}
+			finally {
+				s.close();
+			}
+			return result;
+		}
+		protected abstract Object getResults(Session s, boolean isSingleResult) throws Exception;
+	}
+
+	protected interface ResultChecker {
+		void check(Object results);	
+	}
+
+	protected abstract CacheMode getQueryCacheMode();
+
+	protected boolean areDynamicNonLazyAssociationsChecked() {
+		return true;
+	}
+
+	protected void createData() {
+		Session s = openSession();
+		Transaction t = s.beginTransaction();
+
+		courseExpected = new Course();
+		courseExpected.setCourseCode( "HIB" );
+		courseExpected.setDescription( "Hibernate Training" );
+		courseMeetingExpected1 = new CourseMeeting( courseExpected, "Monday", 1, "1313 Mockingbird Lane" );
+		courseMeetingExpected2 = new CourseMeeting( courseExpected, "Tuesday", 2, "1313 Mockingbird Lane" );
+		courseExpected.getCourseMeetings().add( courseMeetingExpected1 );
+		courseExpected.getCourseMeetings().add( courseMeetingExpected2 );
+		s.save( courseExpected );
+
+		yogiExpected = new Student();
+		yogiExpected.setName( "Yogi" );
+		yogiExpected.setStudentNumber( 111 );
+		yogiExpected.setPreferredCourse( courseExpected );
+		List yogiSecretCodes = new ArrayList();
+		yogiSecretCodes.add( Integer.valueOf( 0 ) );
+		yogiExpected.setSecretCodes( yogiSecretCodes );
+		s.save( yogiExpected );
+
+		Address address1 = new Address( yogiExpected, "home", "1 Main Street", "Podunk", "WA", "98000", "USA" );
+		Address address2 = new Address( yogiExpected, "work", "2 Main Street", "NotPodunk", "WA", "98001", "USA" );
+		yogiExpected.getAddresses().put( address1.getAddressType(), address1 );
+		yogiExpected.getAddresses().put( address2.getAddressType(), address2  );		
+		s.save( address1 );
+		s.save( address2 );
+
+		shermanExpected = new Student();
+		shermanExpected.setName( "Sherman" );
+		shermanExpected.setStudentNumber( 999 );
+		List shermanSecretCodes = new ArrayList();
+		shermanSecretCodes.add( Integer.valueOf( 1 ) );
+		shermanSecretCodes.add( Integer.valueOf( 2 ) );
+		shermanExpected.setSecretCodes( shermanSecretCodes );
+		s.save( shermanExpected );
+
+		shermanEnrolmentExpected = new Enrolment();
+		shermanEnrolmentExpected.setCourse( courseExpected );
+		shermanEnrolmentExpected.setCourseCode( courseExpected.getCourseCode() );
+		shermanEnrolmentExpected.setSemester( ( short ) 1 );
+		shermanEnrolmentExpected.setYear( ( short ) 1999 );
+		shermanEnrolmentExpected.setStudent( shermanExpected );
+		shermanEnrolmentExpected.setStudentNumber( shermanExpected.getStudentNumber() );
+		shermanExpected.getEnrolments().add( shermanEnrolmentExpected );
+		s.save( shermanEnrolmentExpected );
+
+		yogiEnrolmentExpected = new Enrolment();
+		yogiEnrolmentExpected.setCourse( courseExpected );
+		yogiEnrolmentExpected.setCourseCode( courseExpected.getCourseCode() );
+		yogiEnrolmentExpected.setSemester( ( short ) 3 );
+		yogiEnrolmentExpected.setYear( ( short ) 1998 );
+		yogiEnrolmentExpected.setStudent( yogiExpected );
+		yogiEnrolmentExpected.setStudentNumber( yogiExpected.getStudentNumber() );
+		yogiExpected.getEnrolments().add( yogiEnrolmentExpected );
+		s.save( yogiEnrolmentExpected );
+
+		t.commit();
+		s.close();
+	}
+
+	protected void deleteData() {
+		Session s = openSession();
+		Transaction t = s.beginTransaction();
+/*
+		List students = s.createQuery( "from Student" ).list();
+		for ( Iterator it = students.iterator(); it.hasNext(); ) {
+			s.delete( it.next() );
+		}
+		s.createQuery( "delete from Enrolment" ).executeUpdate();
+		s.createQuery( "delete from CourseMeeting" ).executeUpdate();
+		s.createQuery( "delete from Course" ).executeUpdate();
+*/
+		s.delete( yogiExpected );
+		s.delete( shermanExpected );
+		s.delete( yogiEnrolmentExpected );
+		s.delete( shermanEnrolmentExpected );
+		s.delete( courseMeetingExpected1 );
+		s.delete( courseMeetingExpected2 );
+		s.delete( courseExpected );
+		t.commit();
+		s.close();
+	}
+
+
+	public void testAliasToEntityMapNoProjectionList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Student.class, "s" )
+						.createAlias( "s.enrolments", "e", Criteria.LEFT_JOIN )
+						.createAlias( "e.course", "c", Criteria.LEFT_JOIN )
+								.setResultTransformer( Criteria.ALIAS_TO_ENTITY_MAP )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "from Student s left join s.enrolments e left join e.course c order by s.studentNumber" )
+						.setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Map yogiMap = ( Map ) resultList.get( 0 );
+				assertEquals( 3, yogiMap.size() );
+				Map shermanMap = ( Map ) resultList.get( 1 );
+				assertEquals( 3, shermanMap.size() );
+				assertEquals( yogiExpected, yogiMap.get( "s" ) );
+				assertEquals( yogiEnrolmentExpected, yogiMap.get( "e" ) );
+				assertEquals( courseExpected, yogiMap.get( "c" ) );
+				assertEquals( shermanExpected, shermanMap.get( "s" ) );
+				assertEquals( shermanEnrolmentExpected, shermanMap.get( "e" ) );
+				assertEquals( courseExpected, shermanMap.get( "c" ) );
+				assertSame( ( ( Map ) resultList.get( 0 ) ).get( "c" ), shermanMap.get( "c" ) );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testAliasToEntityMapNoProjectionMultiAndNullList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Student.class, "s" )
+						.createAlias( "s.preferredCourse", "p", Criteria.LEFT_JOIN )
+						.createAlias( "s.addresses", "a", Criteria.LEFT_JOIN )
+								.setResultTransformer( Criteria.ALIAS_TO_ENTITY_MAP )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "from Student s left join s.preferredCourse p left join s.addresses a order by s.studentNumber" )
+						.setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 3, resultList.size() );
+				Map yogiMap1 = ( Map ) resultList.get( 0 );
+				assertEquals( 3, yogiMap1.size() );
+				Map yogiMap2 = ( Map ) resultList.get( 1 );
+				assertEquals( 3, yogiMap2.size() );
+				Map shermanMap = ( Map ) resultList.get( 2 );
+				assertEquals( 3, shermanMap.size() );
+				assertEquals( yogiExpected, yogiMap1.get( "s" ) );
+				assertEquals( courseExpected, yogiMap1.get( "p" ) );
+				Address yogiAddress1 = ( Address ) yogiMap1.get( "a" );
+				assertEquals( yogiExpected.getAddresses().get( yogiAddress1.getAddressType() ),
+						yogiMap1.get( "a" ));
+				assertEquals( yogiExpected, yogiMap2.get( "s" ) );
+				assertEquals( courseExpected, yogiMap2.get( "p" ) );
+				Address yogiAddress2 = ( Address ) yogiMap2.get( "a" );
+				assertEquals( yogiExpected.getAddresses().get( yogiAddress2.getAddressType() ),
+						yogiMap2.get( "a" ));
+				assertSame( yogiMap1.get( "s" ), yogiMap2.get( "s" ) );
+				assertSame( yogiMap1.get( "p" ), yogiMap2.get( "p" ) );
+				assertFalse( yogiAddress1.getAddressType().equals( yogiAddress2.getAddressType() ) );
+				assertEquals( shermanExpected, shermanMap.get( "s" ) );
+				assertEquals( shermanExpected.getPreferredCourse(), shermanMap.get( "p" ) );
+				assertNull( shermanMap.get( "a") );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}	
+
+	public void testAliasToEntityMapNoProjectionNullAndNonNullAliasList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Student.class, "s" )
+						.createAlias( "s.addresses", "a", Criteria.LEFT_JOIN )
+								.setResultTransformer( Criteria.ALIAS_TO_ENTITY_MAP )
+						.createCriteria( "s.preferredCourse", Criteria.INNER_JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "from Student s left join s.addresses a left join s.preferredCourse order by s.studentNumber" )
+						.setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Map yogiMap1 = ( Map ) resultList.get( 0 );
+				assertEquals( 2, yogiMap1.size() );
+				Map yogiMap2 = ( Map ) resultList.get( 1 );
+				assertEquals( 2, yogiMap2.size() );
+				assertEquals( yogiExpected, yogiMap1.get( "s" ) );
+				Address yogiAddress1 = ( Address ) yogiMap1.get( "a" );
+				assertEquals( yogiExpected.getAddresses().get( yogiAddress1.getAddressType() ),
+						yogiMap1.get( "a" ));
+				assertEquals( yogiExpected, yogiMap2.get( "s" ) );
+				Address yogiAddress2 = ( Address ) yogiMap2.get( "a" );
+				assertEquals( yogiExpected.getAddresses().get( yogiAddress2.getAddressType() ),
+						yogiMap2.get( "a" ));
+				assertSame( yogiMap1.get( "s" ), yogiMap2.get( "s" ) );
+				assertFalse( yogiAddress1.getAddressType().equals( yogiAddress2.getAddressType() ) );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testEntityWithNonLazyOneToManyUnique() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Course.class );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "from Course" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				assertTrue( results instanceof Course );
+				assertEquals( courseExpected, results );
+				assertTrue( Hibernate.isInitialized( ( ( Course ) courseExpected ).getCourseMeetings() ) );
+				assertEquals( courseExpected.getCourseMeetings(), ( ( Course ) courseExpected ).getCourseMeetings() );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, true );
+	}
+
+	public void testEntityWithNonLazyManyToOneList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( CourseMeeting.class )
+						.addOrder( Order.asc( "id.day") );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			protected Query getQuery(Session s) {
+				return s.createQuery( "from CourseMeeting order by id.day" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				assertEquals( courseMeetingExpected1, resultList.get( 0 ) );
+				assertEquals( courseMeetingExpected2, resultList.get( 1 ) );
+				assertTrue( Hibernate.isInitialized( ( ( CourseMeeting ) resultList.get( 0 ) ).getCourse() ) );
+				assertTrue( Hibernate.isInitialized( ( ( CourseMeeting ) resultList.get( 1 ) ).getCourse() ) );
+				assertEquals( courseExpected, ( ( CourseMeeting ) resultList.get( 0 ) ).getCourse() );
+				assertEquals( courseExpected, ( ( CourseMeeting ) resultList.get( 1 ) ).getCourse() );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testEntityWithLazyAssnUnique() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.add( Restrictions.eq( "studentNumber", shermanExpected.getStudentNumber() ) );
+				}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "from Student s where s.studentNumber = :studentNumber" )
+						.setParameter( "studentNumber", shermanExpected.getStudentNumber() );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				assertTrue( results instanceof Student );
+				assertEquals( shermanExpected, results );
+				assertNotNull(  ( ( Student ) results ).getEnrolments() );
+				assertFalse( Hibernate.isInitialized( ( ( Student ) results ).getEnrolments() ) );
+				assertNull( ( ( Student ) results ).getPreferredCourse() );				
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, true );
+	}
+
+	// should use RootEntityTransformer by default
+	public void testEntityWithLazyAssnList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class )
+						.addOrder( Order.asc( "studentNumber") );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "from Student order by studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				assertEquals( yogiExpected, resultList.get( 0 ) );
+				assertEquals( shermanExpected, resultList.get( 1 ) );
+				assertNotNull( ( ( Student ) resultList.get( 0 ) ).getEnrolments() );
+				assertNotNull( ( ( Student ) resultList.get( 0 ) ).getPreferredCourse() );
+				assertNotNull( ( ( Student ) resultList.get( 1 ) ).getEnrolments() );
+				assertNull( ( ( Student ) resultList.get( 1 ) ).getPreferredCourse() );
+				assertFalse( Hibernate.isInitialized( ( ( Student ) resultList.get( 0 ) ).getEnrolments() ) );
+				assertFalse( Hibernate.isInitialized( ( ( Student ) resultList.get( 0 ) ).getPreferredCourse() ) );
+				assertFalse( Hibernate.isInitialized( ( ( Student ) resultList.get( 1 ) ).getEnrolments() ) );
+				assertNull( ( ( Student ) resultList.get( 1 ) ).getPreferredCourse() );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testEntityWithUnaliasedJoinFetchedLazyOneToManySingleElementList() throws Exception {
+		// unaliased
+		CriteriaExecutor criteriaExecutorUnaliased = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.setFetchMode( "enrolments", FetchMode.JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		HqlExecutor hqlExecutorUnaliased = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "from Student s left join fetch s.enrolments order by s.studentNumber" );
+			}
+		};
+
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				assertEquals( yogiExpected, resultList.get( 0 ) );
+				assertEquals( shermanExpected, resultList.get( 1 ) );
+				assertNotNull( ( ( Student ) resultList.get( 0 ) ).getEnrolments() );
+				assertNotNull( ( ( Student ) resultList.get( 1 ) ).getEnrolments() );
+				if ( areDynamicNonLazyAssociationsChecked() ) {
+					assertTrue( Hibernate.isInitialized( ( ( Student ) resultList.get( 0 ) ).getEnrolments() ) );
+					assertEquals( yogiExpected.getEnrolments(), ( ( Student ) resultList.get( 0 ) ).getEnrolments() );
+					assertTrue( Hibernate.isInitialized( ( ( Student ) resultList.get( 1 ) ).getEnrolments() ) );
+					assertEquals( shermanExpected.getEnrolments(), ( ( Student ) resultList.get( 1 ) ).getEnrolments() );
+				}
+			}
+		};
+
+		runTest( hqlExecutorUnaliased, criteriaExecutorUnaliased, checker, false);
+	}
+
+	public void testJoinWithFetchJoinListCriteria() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Student.class, "s" )
+						.createAlias( "s.preferredCourse", "pc", Criteria.LEFT_JOIN  )
+						.setFetchMode( "enrolments", FetchMode.JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );						
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				assertEquals( yogiExpected, resultList.get( 0 ) );
+				// The following fails for criteria due to HHH-3524
+				//assertEquals( yogiExpected.getPreferredCourse(), ( ( Student ) resultList.get( 0 ) ).getPreferredCourse() );
+				assertEquals( yogiExpected.getPreferredCourse().getCourseCode(),
+						( ( Student ) resultList.get( 0 ) ).getPreferredCourse().getCourseCode() );				
+				assertEquals( shermanExpected, resultList.get( 1 ) );
+				assertNull( ( ( Student ) resultList.get( 1 ) ).getPreferredCourse() );
+				if ( areDynamicNonLazyAssociationsChecked() ) {
+					assertTrue( Hibernate.isInitialized( ( ( Student ) resultList.get( 0 ) ).getEnrolments() ) );
+					assertEquals( yogiExpected.getEnrolments(), ( ( Student ) resultList.get( 0 ) ).getEnrolments() );
+					assertTrue( Hibernate.isInitialized( ( ( Student ) resultList.get( 1 ) ).getEnrolments() ) );
+					assertEquals( shermanExpected.getEnrolments(), ( ( ( Student ) resultList.get( 1 ) ).getEnrolments() ) );
+				}
+			}
+		};
+		runTest( null, criteriaExecutor, checker, false );
+	}
+
+	public void testJoinWithFetchJoinListHql() throws Exception {
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "from Student s left join fetch s.enrolments left join s.preferredCourse order by s.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Object[] yogiObjects = ( Object[] ) resultList.get( 0 );
+				assertEquals( yogiExpected, yogiObjects[ 0 ] );
+				assertEquals( yogiExpected.getPreferredCourse(), yogiObjects[ 1 ] );
+				Object[] shermanObjects = ( Object[] ) resultList.get( 1 );
+				assertEquals( shermanExpected, shermanObjects[ 0 ] );
+				assertNull( shermanObjects[ 1 ] );
+				assertNull( ( ( Student ) shermanObjects[ 0 ] ).getPreferredCourse() );
+				if ( areDynamicNonLazyAssociationsChecked() ) {
+					assertTrue( Hibernate.isInitialized( ( ( Student )  yogiObjects[ 0 ] ).getEnrolments() ) );
+					assertEquals( yogiExpected.getEnrolments(), ( ( Student ) yogiObjects[ 0 ] ).getEnrolments() );
+					assertTrue( Hibernate.isInitialized( ( ( Student ) shermanObjects[ 0 ] ).getEnrolments() ) );
+					assertEquals( shermanExpected.getEnrolments(), ( ( ( Student ) shermanObjects[ 0 ] ).getEnrolments() ) );
+				}
+			}
+		};
+		runTest( hqlExecutor, null, checker, false );
+	}
+
+	public void testJoinWithFetchJoinWithOwnerAndPropProjectedList() throws Exception {
+		HqlExecutor hqlSelectNewMapExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select s, s.name from Student s left join fetch s.enrolments left join s.preferredCourse order by s.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Object[] yogiObjects = ( Object[] ) resultList.get( 0 );
+				assertEquals( yogiExpected, yogiObjects[ 0 ] );
+				assertEquals( yogiExpected.getName(), yogiObjects[ 1 ] );
+				Object[] shermanObjects = ( Object[] ) resultList.get( 1 );
+				assertEquals( shermanExpected, shermanObjects[ 0 ] );
+				assertEquals( shermanExpected.getName(), shermanObjects[ 1 ] );
+				if ( areDynamicNonLazyAssociationsChecked() ) {
+					assertTrue( Hibernate.isInitialized( ( ( Student )  yogiObjects[ 0 ] ).getEnrolments() ) );
+					assertEquals( yogiExpected.getEnrolments(), ( ( Student ) yogiObjects[ 0 ] ).getEnrolments() );
+					assertTrue( Hibernate.isInitialized( ( ( Student ) shermanObjects[ 0 ] ).getEnrolments() ) );
+					assertEquals( shermanExpected.getEnrolments(), ( ( ( Student ) shermanObjects[ 0 ] ).getEnrolments() ) );
+				}
+			}
+		};
+		runTest( hqlSelectNewMapExecutor, null, checker, false );
+	}
+
+	public void testJoinWithFetchJoinWithPropAndOwnerProjectedList() throws Exception {
+		HqlExecutor hqlSelectNewMapExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select s.name, s from Student s left join fetch s.enrolments left join s.preferredCourse order by s.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Object[] yogiObjects = ( Object[] ) resultList.get( 0 );
+				assertEquals( yogiExpected.getName(), yogiObjects[ 0 ] );
+				assertEquals( yogiExpected, yogiObjects[ 1 ] );
+				Object[] shermanObjects = ( Object[] ) resultList.get( 1 );
+				assertEquals( shermanExpected.getName(), shermanObjects[ 0 ] );
+				assertEquals( shermanExpected, shermanObjects[ 1 ] );
+				if ( areDynamicNonLazyAssociationsChecked() ) {
+					assertTrue( Hibernate.isInitialized( ( ( Student )  yogiObjects[ 1 ] ).getEnrolments() ) );
+					assertEquals( yogiExpected.getEnrolments(), ( ( Student ) yogiObjects[ 1 ] ).getEnrolments() );
+					assertTrue( Hibernate.isInitialized( ( ( Student ) shermanObjects[ 1 ] ).getEnrolments() ) );
+					assertEquals( shermanExpected.getEnrolments(), ( ( ( Student ) shermanObjects[ 1 ] ).getEnrolments() ) );
+				}
+			}
+		};
+		runTest( hqlSelectNewMapExecutor, null, checker, false );
+	}
+
+	public void testJoinWithFetchJoinWithOwnerAndAliasedJoinedProjectedListHql() throws Exception {
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select s, pc from Student s left join fetch s.enrolments left join s.preferredCourse pc order by s.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Object[] yogiObjects = ( Object[] ) resultList.get( 0 );
+				assertEquals( yogiExpected, yogiObjects[ 0 ] );
+				assertEquals(
+						yogiExpected.getPreferredCourse().getCourseCode(),
+						( ( Course ) yogiObjects[ 1 ] ).getCourseCode()
+				);
+				Object[] shermanObjects = ( Object[]  ) resultList.get( 1 );
+				assertEquals( shermanExpected, shermanObjects[ 0 ] );
+				assertNull( shermanObjects[ 1 ] );
+				if ( areDynamicNonLazyAssociationsChecked() ) {
+					assertEquals( yogiExpected.getPreferredCourse(), yogiObjects[ 1 ] );
+					assertTrue( Hibernate.isInitialized( ( ( Student ) yogiObjects[ 0 ] ).getEnrolments() ) );
+					assertEquals( yogiExpected.getEnrolments(), ( ( Student ) yogiObjects[ 0 ] ).getEnrolments() );
+					assertTrue( Hibernate.isInitialized( ( ( Student ) shermanObjects[ 0 ] ).getEnrolments() ) );
+					assertEquals( shermanExpected.getEnrolments(), ( ( ( Student ) shermanObjects[ 0 ] ).getEnrolments() ) );
+				}
+			}
+		};
+		runTest( hqlExecutor, null, checker, false );
+	}
+
+	public void testJoinWithFetchJoinWithAliasedJoinedAndOwnerProjectedListHql() throws Exception {
+		HqlExecutor hqlSelectNewMapExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select pc, s from Student s left join fetch s.enrolments left join s.preferredCourse pc order by s.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Object[] yogiObjects = ( Object[] ) resultList.get( 0 );
+				assertEquals( yogiExpected, yogiObjects[ 1 ] );
+				assertEquals(
+						yogiExpected.getPreferredCourse().getCourseCode(),
+						( ( Course ) yogiObjects[ 0 ] ).getCourseCode()
+				);
+				Object[] shermanObjects = ( Object[]  ) resultList.get( 1 );
+				assertEquals( shermanExpected, shermanObjects[ 1 ] );
+				assertNull( shermanObjects[ 0 ] );
+				if ( areDynamicNonLazyAssociationsChecked() ) {
+					assertEquals( yogiExpected.getPreferredCourse(), yogiObjects[ 0 ] );
+					assertTrue( Hibernate.isInitialized( ( ( Student ) yogiObjects[ 1 ] ).getEnrolments() ) );
+					assertEquals( yogiExpected.getEnrolments(), ( ( Student ) yogiObjects[ 1 ] ).getEnrolments() );
+					assertTrue( Hibernate.isInitialized( ( ( Student ) shermanObjects[ 1 ] ).getEnrolments() ) );
+					assertEquals( shermanExpected.getEnrolments(), ( ( ( Student ) shermanObjects[ 1 ] ).getEnrolments() ) );
+				}
+			}
+		};
+		runTest( hqlSelectNewMapExecutor, null, checker, false );
+	}
+
+	public void testEntityWithAliasedJoinFetchedLazyOneToManySingleElementListHql() throws Exception {
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "from Student s left join fetch s.enrolments e order by s.studentNumber" );
+			}
+		};
+
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				assertEquals( yogiExpected, resultList.get( 0 ) );
+				assertEquals(
+						yogiExpected.getPreferredCourse().getCourseCode(),
+						( ( Student ) resultList.get( 0 ) ).getPreferredCourse().getCourseCode()
+				);
+				assertEquals( shermanExpected, resultList.get( 1 ) );
+				assertNull( ( ( Student ) resultList.get( 1 ) ).getPreferredCourse() );
+				if ( areDynamicNonLazyAssociationsChecked() ) {
+					assertTrue( Hibernate.isInitialized( ( ( Student ) resultList.get( 0 ) ).getEnrolments() ) );
+					assertEquals( yogiExpected.getEnrolments(), ( ( Student ) resultList.get( 0 ) ).getEnrolments() );
+					assertTrue( Hibernate.isInitialized( ( ( Student ) resultList.get( 1 ) ).getEnrolments() ) );
+					assertEquals( shermanExpected.getEnrolments(), ( ( ( Student ) resultList.get( 1 ) ).getEnrolments() ) );
+				}
+			}
+		};
+
+		runTest( hqlExecutor, null, checker, false);
+	}
+
+	public void testEntityWithSelectFetchedLazyOneToManySingleElementListCriteria() throws Exception {
+		CriteriaExecutor criteriaExecutorUnaliased = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.setFetchMode( "enrolments", FetchMode.SELECT )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				assertEquals( yogiExpected, resultList.get( 0 ) );
+				assertEquals( shermanExpected, resultList.get( 1 ) );
+				assertNotNull( ( ( Student ) resultList.get( 0 ) ).getEnrolments() );
+				assertFalse( Hibernate.isInitialized( ( ( Student ) resultList.get( 0 ) ).getEnrolments() ) );
+				assertNotNull( ( ( Student ) resultList.get( 1 ) ).getEnrolments() );
+				assertFalse( Hibernate.isInitialized( ( ( Student ) resultList.get( 1 ) ).getEnrolments() ) );
+			}
+		};
+
+		runTest( null, criteriaExecutorUnaliased, checker, false);
+	}
+
+	public void testEntityWithJoinFetchedLazyOneToManyMultiAndNullElementList() throws Exception {
+		//unaliased
+		CriteriaExecutor criteriaExecutorUnaliased = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.setFetchMode( "addresses", FetchMode.JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		HqlExecutor hqlExecutorUnaliased = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "from Student s left join fetch s.addresses order by s.studentNumber" );
+			}
+		};
+
+		//aliased
+		CriteriaExecutor criteriaExecutorAliased1 = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.createAlias( "s.addresses", "a", Criteria.LEFT_JOIN )
+						.setFetchMode( "addresses", FetchMode.JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		CriteriaExecutor criteriaExecutorAliased2 = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.createAlias( "s.addresses", "a", Criteria.LEFT_JOIN )
+						.setFetchMode( "a", FetchMode.JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		CriteriaExecutor criteriaExecutorAliased3 = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.createCriteria( "s.addresses", "a", Criteria.LEFT_JOIN )
+						.setFetchMode( "addresses", FetchMode.JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		CriteriaExecutor criteriaExecutorAliased4 = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.createCriteria( "s.addresses", "a", Criteria.LEFT_JOIN )
+						.setFetchMode( "a", FetchMode.JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		HqlExecutor hqlExecutorAliased = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "from Student s left join fetch s.addresses a order by s.studentNumber" );
+			}
+		};
+
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 3, resultList.size() );
+				assertEquals( yogiExpected, resultList.get( 0 ) );
+				assertSame( resultList.get( 0 ), resultList.get( 1 ) );
+				assertEquals( shermanExpected, resultList.get( 2 ) );
+				assertNotNull( ( ( Student ) resultList.get( 0 ) ).getAddresses() );
+				assertNotNull( ( ( Student ) resultList.get( 1 ) ).getAddresses() );
+				assertNotNull( ( ( Student ) resultList.get( 2 ) ).getAddresses() );
+				if ( areDynamicNonLazyAssociationsChecked() ) {
+					assertTrue( Hibernate.isInitialized( ( ( Student ) resultList.get( 0 ) ).getAddresses() ) );
+					assertEquals( yogiExpected.getAddresses(), ( ( Student ) resultList.get( 0 ) ).getAddresses() );
+					assertTrue( ( ( Student ) resultList.get( 2 ) ).getAddresses().isEmpty() );
+				}
+			}
+		};
+		runTest( hqlExecutorUnaliased, criteriaExecutorUnaliased, checker, false );
+		runTest( hqlExecutorAliased, criteriaExecutorAliased1, checker, false );
+		runTest( null, criteriaExecutorAliased2, checker, false );
+		runTest( null, criteriaExecutorAliased3, checker, false );
+		runTest( null, criteriaExecutorAliased4, checker, false );
+	}
+
+	public void testEntityWithJoinFetchedLazyManyToOneList() throws Exception {
+		// unaliased
+		CriteriaExecutor criteriaExecutorUnaliased = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.setFetchMode( "preferredCourse", FetchMode.JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		HqlExecutor hqlExecutorUnaliased = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "from Student s left join fetch s.preferredCourse order by s.studentNumber" );
+			}
+		};
+
+		// aliased
+		CriteriaExecutor criteriaExecutorAliased1 = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.createAlias( "s.preferredCourse", "pCourse", Criteria.LEFT_JOIN )
+						.setFetchMode( "preferredCourse", FetchMode.JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		CriteriaExecutor criteriaExecutorAliased2 = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.createAlias( "s.preferredCourse", "pCourse", Criteria.LEFT_JOIN )
+						.setFetchMode( "pCourse", FetchMode.JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		CriteriaExecutor criteriaExecutorAliased3 = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.createCriteria( "s.preferredCourse", "pCourse", Criteria.LEFT_JOIN )
+						.setFetchMode( "preferredCourse", FetchMode.JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		CriteriaExecutor criteriaExecutorAliased4 = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.createCriteria( "s.preferredCourse", "pCourse", Criteria.LEFT_JOIN )
+						.setFetchMode( "pCourse", FetchMode.JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		HqlExecutor hqlExecutorAliased = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "from Student s left join fetch s.preferredCourse pCourse order by s.studentNumber" );
+			}
+		};
+
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				assertEquals( yogiExpected, resultList.get( 0 ) );
+				assertEquals( shermanExpected, resultList.get( 1 ) );
+				assertEquals( yogiExpected.getPreferredCourse().getCourseCode(),
+						( ( Student ) resultList.get( 0 ) ).getPreferredCourse().getCourseCode() );
+				assertNull( ( ( Student ) resultList.get( 1 ) ).getPreferredCourse() );
+			}
+		};
+		runTest( hqlExecutorUnaliased, criteriaExecutorUnaliased, checker, false );
+		runTest( hqlExecutorAliased, criteriaExecutorAliased1, checker, false );
+		runTest( null, criteriaExecutorAliased2, checker, false );
+		runTest( null, criteriaExecutorAliased3, checker, false );
+		runTest( null, criteriaExecutorAliased4, checker, false );
+	}
+
+	public void testEntityWithJoinFetchedLazyManyToOneUsingProjectionList() throws Exception {
+		// unaliased
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Enrolment.class, "e" )
+						.createAlias( "e.student", "s", Criteria.LEFT_JOIN )
+						.setFetchMode( "student", FetchMode.JOIN )
+						.setFetchMode( "student.preferredCourse", FetchMode.JOIN )
+						.setProjection(
+								Projections.projectionList()
+										.add( Projections.property( "s.name" ) )
+										.add( Projections.property( "e.student" ) )
+						)
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select s.name, s from Enrolment e left join e.student s left join fetch s.preferredCourse order by s.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Object[] yogiObjects = ( Object[] ) resultList.get( 0 );
+				Object[] shermanObjects = ( Object[] ) resultList.get( 1 );
+				assertEquals( yogiExpected.getName(), yogiObjects[ 0 ] );
+				assertEquals( shermanExpected.getName(), shermanObjects[ 0 ] );
+				// The following fails for criteria due to HHH-1425
+				// assertEquals( yogiExpected, yogiObjects[ 1 ] );
+				// assertEquals( shermanExpected, shermanObjects[ 1 ] );
+				assertEquals( yogiExpected.getStudentNumber(), ( ( Student ) yogiObjects[ 1 ] ).getStudentNumber() );
+				assertEquals( shermanExpected.getStudentNumber(), ( ( Student ) shermanObjects[ 1 ] ).getStudentNumber() );
+				if ( areDynamicNonLazyAssociationsChecked() ) {
+					// The following fails for criteria due to HHH-1425
+					//assertTrue( Hibernate.isInitialized( ( ( Student ) yogiObjects[ 1 ] ).getPreferredCourse() ) );
+					//assertEquals( yogiExpected.getPreferredCourse(),  ( ( Student ) yogiObjects[ 1 ] ).getPreferredCourse() );
+					//assertTrue( Hibernate.isInitialized( ( ( Student ) shermanObjects[ 1 ] ).getPreferredCourse() ) );
+					//assertEquals( shermanExpected.getPreferredCourse(),  ( ( Student ) shermanObjects[ 1 ] ).getPreferredCourse() );
+				}
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}	
+
+	public void testEntityWithJoinedLazyOneToManySingleElementListCriteria() throws Exception {
+		CriteriaExecutor criteriaExecutorUnaliased = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.createCriteria( "s.enrolments", Criteria.LEFT_JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		CriteriaExecutor criteriaExecutorAliased1 = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.createCriteria( "s.enrolments", "e", Criteria.LEFT_JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		CriteriaExecutor criteriaExecutorAliased2 = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.createAlias( "s.enrolments", "e", Criteria.LEFT_JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				assertEquals( yogiExpected, resultList.get( 0 ) );
+				assertEquals( shermanExpected, resultList.get( 1 ) );
+				assertNotNull( ( ( Student ) resultList.get( 0 ) ).getEnrolments() );
+				assertNotNull( ( ( Student ) resultList.get( 1 ) ).getEnrolments() );
+				if ( areDynamicNonLazyAssociationsChecked() ) {
+					assertTrue( Hibernate.isInitialized( ( ( Student ) resultList.get( 0 ) ).getEnrolments() ) );
+					assertEquals( yogiExpected.getEnrolments(), ( ( Student ) resultList.get( 0 ) ).getEnrolments() );
+					assertTrue( Hibernate.isInitialized( ( ( Student ) resultList.get( 1 ) ).getEnrolments() ) );
+					assertEquals( shermanExpected.getEnrolments(), ( ( Student ) resultList.get( 1 ) ).getEnrolments() );
+				}
+			}
+		};
+		runTest( null, criteriaExecutorUnaliased, checker, false );
+		runTest( null, criteriaExecutorAliased1, checker, false );
+		runTest( null, criteriaExecutorAliased2, checker, false );		
+	}
+
+	public void testEntityWithJoinedLazyOneToManyMultiAndNullListCriteria() throws Exception {
+		CriteriaExecutor criteriaExecutorUnaliased = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.createCriteria( "s.addresses", Criteria.LEFT_JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		CriteriaExecutor criteriaExecutorAliased1 = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.createCriteria( "s.addresses", "a", Criteria.LEFT_JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		CriteriaExecutor criteriaExecutorAliased2 = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.createAlias( "s.addresses", "a", Criteria.LEFT_JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 3, resultList.size() );
+				assertEquals( yogiExpected, resultList.get( 0 ) );
+				assertSame( resultList.get( 0 ), resultList.get( 1 ) );
+				assertEquals( shermanExpected, resultList.get( 2 ) );
+				assertNotNull( ( ( Student ) resultList.get( 0 ) ).getAddresses() );
+				assertNotNull( ( ( Student ) resultList.get( 2 ) ).getAddresses() );
+				assertNotNull( ( ( Student ) resultList.get( 1 ) ).getAddresses() );
+				if ( areDynamicNonLazyAssociationsChecked() ) {
+					assertTrue( Hibernate.isInitialized( ( ( Student ) resultList.get( 0 ) ).getAddresses() ) );
+					assertEquals( yogiExpected.getAddresses(), ( ( Student ) resultList.get( 0 ) ).getAddresses() );
+					assertTrue( ( ( Student ) resultList.get( 2 ) ).getAddresses().isEmpty() );
+				}
+			}
+		};
+		runTest( null, criteriaExecutorUnaliased, checker, false );
+		runTest( null, criteriaExecutorAliased1, checker, false );
+		runTest( null, criteriaExecutorAliased2, checker, false );
+	}
+	
+	public void testEntityWithJoinedLazyManyToOneListCriteria() throws Exception {
+		CriteriaExecutor criteriaExecutorUnaliased = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.createCriteria( "s.preferredCourse", Criteria.LEFT_JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		CriteriaExecutor criteriaExecutorAliased1 = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.createCriteria( "s.preferredCourse", "p", Criteria.LEFT_JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		CriteriaExecutor criteriaExecutorAliased2 = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.createAlias( "s.preferredCourse", "p", Criteria.LEFT_JOIN )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				assertEquals( yogiExpected, resultList.get( 0 ) );
+				assertEquals( shermanExpected, resultList.get( 1 ) );
+				assertEquals( yogiExpected.getPreferredCourse().getCourseCode(),
+						( ( Student ) resultList.get( 0 ) ).getPreferredCourse().getCourseCode() );
+				assertNull( ( ( Student ) resultList.get( 1 ) ).getPreferredCourse() );
+			}
+		};
+		runTest( null, criteriaExecutorUnaliased, checker, false );
+		runTest( null, criteriaExecutorAliased1, checker, false );
+		runTest( null, criteriaExecutorAliased2, checker, false );
+	}
+
+	public void testEntityWithJoinedLazyOneToManySingleElementListHql() throws Exception {
+		HqlExecutor hqlExecutorUnaliased = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "from Student s left join s.enrolments order by s.studentNumber" );
+			}
+		};
+		HqlExecutor hqlExecutorAliased = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "from Student s left join s.enrolments e order by s.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				assertTrue( resultList.get( 0 ) instanceof Object[] );
+				Object[] yogiObjects = ( Object[] ) resultList.get( 0 );
+				assertEquals( yogiExpected, yogiObjects[ 0 ] );
+				assertEquals( yogiEnrolmentExpected, yogiObjects[ 1 ] );
+				assertTrue( resultList.get( 0 ) instanceof Object[] );
+				Object[] shermanObjects = ( Object[] ) resultList.get( 1 );
+				assertEquals( shermanExpected, shermanObjects[ 0 ] );
+				assertEquals( shermanEnrolmentExpected, shermanObjects[ 1 ] );
+			}
+		};
+		runTest( hqlExecutorUnaliased, null, checker, false );
+		runTest( hqlExecutorAliased, null, checker, false );
+	}
+
+	public void testEntityWithJoinedLazyOneToManyMultiAndNullListHql() throws Exception {
+		HqlExecutor hqlExecutorUnaliased = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "from Student s left join s.addresses order by s.studentNumber" );
+			}
+		};
+		HqlExecutor hqlExecutorAliased = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "from Student s left join s.addresses a order by s.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 3, resultList.size() );
+				assertTrue( resultList.get( 0 ) instanceof Object[] );
+				Object[] yogiObjects1 = ( Object[] ) resultList.get( 0 );
+				assertEquals( yogiExpected, yogiObjects1[ 0 ] );
+				Address address1 = ( Address ) yogiObjects1[ 1 ];
+				assertEquals( yogiExpected.getAddresses().get( address1.getAddressType() ), address1 );
+				Object[] yogiObjects2 = ( Object[] ) resultList.get( 1 );
+				assertSame( yogiObjects1[ 0 ], yogiObjects2[ 0 ] );
+				Address address2 = ( Address ) yogiObjects2[ 1 ];
+				assertEquals( yogiExpected.getAddresses().get( address2.getAddressType() ), address2 );
+				assertFalse( address1.getAddressType().equals( address2.getAddressType() ) );
+				Object[] shermanObjects = ( Object[] ) resultList.get( 2 );
+				assertEquals( shermanExpected, shermanObjects[ 0 ] );
+				assertNull( shermanObjects[ 1 ] );
+			}
+		};
+		runTest( hqlExecutorUnaliased, null, checker, false );
+		runTest( hqlExecutorAliased, null, checker, false );
+	}
+
+	public void testEntityWithJoinedLazyManyToOneListHql() throws Exception {
+		HqlExecutor hqlExecutorUnaliased = new HqlExecutor() {
+			protected Query getQuery(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createQuery( "from Student s left join s.preferredCourse order by s.studentNumber" );
+			}
+		};
+		HqlExecutor hqlExecutorAliased = new HqlExecutor() {
+			protected Query getQuery(Session s) {
+				// should use RootEntityTransformer by default
+				return s.createQuery( "from Student s left join s.preferredCourse p order by s.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Object[] yogiObjects = ( Object[] ) resultList.get( 0 );
+				assertEquals( yogiExpected, yogiObjects[ 0 ] );
+				assertEquals( yogiExpected.getPreferredCourse(), yogiObjects[ 1 ] );
+				Object[] shermanObjects = ( Object[] ) resultList.get( 1 );
+				assertEquals( shermanExpected, shermanObjects[ 0 ] );
+				assertNull( shermanObjects[ 1 ] );
+			}
+		};
+		runTest( hqlExecutorUnaliased, null, checker, false );
+		runTest( hqlExecutorAliased, null, checker, false );
+	}
+
+	public void testAliasToEntityMapOneProjectionList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Enrolment.class, "e" )
+						.setProjection( Projections.property( "e.student" ).as( "student" ) )
+						.addOrder( Order.asc( "e.studentNumber") )
+						.setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select e.student as student from Enrolment e order by e.studentNumber" )
+						.setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Map yogiMap = ( Map ) resultList.get( 0 );
+				Map shermanMap = ( Map ) resultList.get( 1 );
+				assertEquals( 1, yogiMap.size() );
+				assertEquals( 1, shermanMap.size() );
+				// TODO: following are initialized for hql and uninitialied for criteria; why?
+				// assertFalse( Hibernate.isInitialized( yogiMap.get( "student" ) ) );
+				// assertFalse( Hibernate.isInitialized( shermanMap.get( "student" ) ) );
+				assertTrue( yogiMap.get( "student" ) instanceof Student );
+				assertTrue( shermanMap.get( "student" ) instanceof Student );
+				assertEquals( yogiExpected.getStudentNumber(), ( ( Student ) yogiMap.get( "student" ) ).getStudentNumber() );
+				assertEquals( shermanExpected.getStudentNumber(), ( ( Student ) shermanMap.get( "student" ) ).getStudentNumber() );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false);
+	}
+
+	public void testAliasToEntityMapMultiProjectionList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Enrolment.class, "e" )
+						.setProjection(
+								Projections.projectionList()
+										.add( Property.forName( "e.student" ), "student" )
+										.add( Property.forName( "e.semester" ), "semester" )
+										.add( Property.forName( "e.year" ), "year" )
+										.add( Property.forName( "e.course" ), "course" )
+						)
+						.addOrder( Order.asc( "studentNumber") )
+						.setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select e.student as student, e.semester as semester, e.year as year, e.course as course from Enrolment e order by e.studentNumber" )
+						.setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Map yogiMap = ( Map ) resultList.get( 0 );
+				Map shermanMap = ( Map ) resultList.get( 1 );
+				assertEquals( 4, yogiMap.size() );
+				assertEquals( 4, shermanMap.size() );
+				assertTrue( yogiMap.get( "student" ) instanceof Student );
+				assertTrue( shermanMap.get( "student" ) instanceof Student );
+				// TODO: following are initialized for hql and uninitialied for criteria; why?
+				// assertFalse( Hibernate.isInitialized( yogiMap.get( "student" ) ) );
+				// assertFalse( Hibernate.isInitialized( shermanMap.get( "student" ) ) );
+				assertEquals( yogiExpected.getStudentNumber(), ( ( Student ) yogiMap.get( "student" ) ).getStudentNumber() );
+				assertEquals( shermanExpected.getStudentNumber(), ( ( Student ) shermanMap.get( "student" ) ).getStudentNumber() );
+				assertEquals( yogiEnrolmentExpected.getSemester(), yogiMap.get( "semester" ) );
+				assertEquals( yogiEnrolmentExpected.getYear(), yogiMap.get( "year" )  );
+				assertEquals( courseExpected, yogiMap.get( "course" ) );
+				assertEquals( shermanEnrolmentExpected.getSemester(), shermanMap.get( "semester" ) );
+				assertEquals( shermanEnrolmentExpected.getYear(), shermanMap.get( "year" )  );
+				assertEquals( courseExpected, shermanMap.get( "course" ) );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testAliasToEntityMapMultiProjectionWithNullAliasList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Enrolment.class, "e" )
+						.setProjection(
+								Projections.projectionList()
+										.add( Property.forName( "e.student" ), "student" )
+										.add( Property.forName( "e.semester" ) )
+										.add( Property.forName( "e.year" ) )
+										.add( Property.forName( "e.course" ), "course" )
+						)
+						.addOrder( Order.asc( "e.studentNumber") )
+						.setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select e.student as student, e.semester, e.year, e.course as course from Enrolment e order by e.studentNumber" )
+						.setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Map yogiMap = ( Map ) resultList.get( 0 );
+				Map shermanMap = ( Map ) resultList.get( 1 );
+				// TODO: following are initialized for hql and uninitialied for criteria; why?
+				// assertFalse( Hibernate.isInitialized( yogiMap.get( "student" ) ) );
+				// assertFalse( Hibernate.isInitialized( shermanMap.get( "student" ) ) );
+				assertTrue( yogiMap.get( "student" ) instanceof Student );
+				assertEquals( yogiExpected.getStudentNumber(), ( ( Student ) yogiMap.get( "student" ) ).getStudentNumber() );
+				assertEquals( shermanExpected.getStudentNumber(), ( ( Student ) shermanMap.get( "student" ) ).getStudentNumber() );
+				assertNull( yogiMap.get( "semester" ) );
+				assertNull( yogiMap.get( "year" )  );
+				assertEquals( courseExpected, yogiMap.get( "course" ) );
+				assertNull( shermanMap.get( "semester" ) );
+				assertNull( shermanMap.get( "year" )  );
+				assertEquals( courseExpected, shermanMap.get( "course" ) );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testAliasToEntityMapMultiAggregatedPropProjectionSingleResult() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Enrolment.class )
+						.setProjection(
+								Projections.projectionList()
+									.add( Projections.min( "studentNumber" ).as( "minStudentNumber" ) )
+									.add( Projections.max( "studentNumber" ).as( "maxStudentNumber" ) )
+						)
+						.setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery(
+						"select min( e.studentNumber ) as minStudentNumber, max( e.studentNumber ) as maxStudentNumber from Enrolment e" )
+						.setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				assertTrue( results instanceof Map );
+				Map resultMap = ( Map ) results;
+				assertEquals( 2, resultMap.size() );
+				assertEquals( yogiExpected.getStudentNumber(), resultMap.get( "minStudentNumber" ) );
+				assertEquals( shermanExpected.getStudentNumber(), resultMap.get( "maxStudentNumber" ) );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, true );
+	}
+
+	public void testOneNonEntityProjectionUnique() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use PassThroughTransformer by default
+				return s.createCriteria( Enrolment.class, "e" )
+						.setProjection( Projections.property( "e.semester" ) )
+						.add( Restrictions.eq( "e.studentNumber", shermanEnrolmentExpected.getStudentNumber() ) );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select e.semester from Enrolment e where e.studentNumber = :studentNumber" )
+						.setParameter( "studentNumber", shermanEnrolmentExpected.getStudentNumber() );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				assertTrue( results instanceof Short );
+				assertEquals( Short.valueOf( shermanEnrolmentExpected.getSemester() ), results );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, true );
+	}
+
+	public void testOneNonEntityProjectionList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use PassThroughTransformer by default
+				return s.createCriteria( Enrolment.class, "e" )
+						.setProjection( Projections.property( "e.semester" ) )
+						.addOrder( Order.asc( "e.studentNumber") );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select e.semester from Enrolment e order by e.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				assertEquals( yogiEnrolmentExpected.getSemester(), resultList.get( 0 ) );
+				assertEquals( shermanEnrolmentExpected.getSemester(), resultList.get( 1 ) );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testListElementsProjectionList() throws Exception {
+		/*
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use PassThroughTransformer by default
+				return s.createCriteria( Student.class, "s" )
+						.createCriteria( "s.secretCodes" )
+						.setProjection( Projections.property( "s.secretCodes" ) )
+						.addOrder( Order.asc( "s.studentNumber") );
+			}
+		};
+		*/
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select elements(s.secretCodes) from Student s" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 3, resultList.size() );
+				assertTrue( resultList.contains( yogiExpected.getSecretCodes().get( 0 ) ) );
+				assertTrue( resultList.contains( shermanExpected.getSecretCodes().get( 0 ) ) );
+				assertTrue( resultList.contains( shermanExpected.getSecretCodes().get( 1 ) ) );
+			}
+		};
+		runTest( hqlExecutor, null, checker, false );
+	}
+
+	public void testOneEntityProjectionUnique() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use PassThroughTransformer by default
+				return s.createCriteria( Enrolment.class )
+						.setProjection( Projections.property( "student" ) )
+						.add( Restrictions.eq( "studentNumber", Long.valueOf( yogiExpected.getStudentNumber() ) ) );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select e.student from Enrolment e where e.studentNumber = :studentNumber" )
+						.setParameter( "studentNumber", Long.valueOf( yogiExpected.getStudentNumber() ) );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				assertTrue( results instanceof Student );
+				Student student = ( Student ) results;
+				// TODO: following is initialized for hql and uninitialied for criteria; why?
+				//assertFalse( Hibernate.isInitialized( student ) );
+				assertEquals( yogiExpected.getStudentNumber(), student.getStudentNumber() );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, true );
+	}
+
+	public void testOneEntityProjectionList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			// should use PassThroughTransformer by default
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Enrolment.class, "e" )
+						.setProjection( Projections.property( "e.student" ) )
+						.addOrder( Order.asc( "e.studentNumber") );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select e.student from Enrolment e order by e.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				// TODO: following is initialized for hql and uninitialied for criteria; why?
+				//assertFalse( Hibernate.isInitialized( resultList.get( 0 ) ) );
+				//assertFalse( Hibernate.isInitialized( resultList.get( 1 ) ) );
+				assertEquals( yogiExpected.getStudentNumber(), ( ( Student ) resultList.get( 0 ) ).getStudentNumber() );
+				assertEquals( shermanExpected.getStudentNumber(), ( ( Student ) resultList.get( 1 ) ).getStudentNumber() );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testMultiEntityProjectionUnique() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use PassThroughTransformer by default
+				return s.createCriteria( Enrolment.class )
+						.setProjection(
+								Projections.projectionList()
+										.add( Property.forName( "student" ) )
+										.add( Property.forName( "semester" ) )
+										.add( Property.forName( "year" ) )
+										.add( Property.forName( "course" ) )
+						)
+						.add( Restrictions.eq( "studentNumber", Long.valueOf( shermanEnrolmentExpected.getStudentNumber() ) ) );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery(
+						"select e.student, e.semester, e.year, e.course from Enrolment e  where e.studentNumber = :studentNumber" )
+						.setParameter( "studentNumber", shermanEnrolmentExpected.getStudentNumber() );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				assertTrue( results instanceof Object[] );
+				Object shermanObjects[] = ( Object [] ) results;
+				assertEquals( 4, shermanObjects.length );
+				assertNotNull( shermanObjects[ 0 ] );
+				assertTrue( shermanObjects[ 0 ] instanceof Student );
+				// TODO: following is initialized for hql and uninitialied for criteria; why?
+				//assertFalse( Hibernate.isInitialized( shermanObjects[ 0 ] ) );
+				assertEquals( shermanEnrolmentExpected.getSemester(), ( (Short) shermanObjects[ 1 ] ).shortValue() );
+				assertEquals( shermanEnrolmentExpected.getYear(), ( (Short) shermanObjects[ 2 ] ).shortValue() );
+				assertTrue( ! ( shermanObjects[ 3 ] instanceof HibernateProxy ) );
+				assertTrue( shermanObjects[ 3 ] instanceof Course );
+				assertEquals( courseExpected, shermanObjects[ 3 ] );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, true );
+	}
+
+	public void testMultiEntityProjectionList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use PassThroughTransformer by default
+				return s.createCriteria( Enrolment.class, "e" )
+						.setProjection(
+								Projections.projectionList()
+										.add( Property.forName( "e.student" ) )
+										.add( Property.forName( "e.semester" ) )
+										.add( Property.forName( "e.year" ) )
+										.add( Property.forName( "e.course" ) )
+						)
+						.addOrder( Order.asc( "e.studentNumber") );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select e.student, e.semester, e.year, e.course from Enrolment e order by e.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Object[] yogiObjects = ( Object[] ) resultList.get( 0 );
+				Object[] shermanObjects = ( Object[] ) resultList.get( 1 );
+				assertEquals( 4, yogiObjects.length );
+				// TODO: following is initialized for hql and uninitialied for criteria; why?
+				//assertFalse( Hibernate.isInitialized( yogiObjects[ 0 ] ) );
+				//assertFalse( Hibernate.isInitialized( shermanObjects[ 0 ] ) );
+				assertTrue( yogiObjects[ 0 ] instanceof Student );
+				assertTrue( shermanObjects[ 0 ] instanceof Student );
+				assertEquals( yogiEnrolmentExpected.getSemester(), ( (Short) yogiObjects[ 1 ] ).shortValue() );
+				assertEquals( yogiEnrolmentExpected.getYear(), ( (Short) yogiObjects[ 2 ] ).shortValue() );
+				assertEquals( courseExpected, yogiObjects[ 3 ] );
+				assertEquals( shermanEnrolmentExpected.getSemester(), ( (Short) shermanObjects[ 1 ] ).shortValue() );
+				assertEquals( shermanEnrolmentExpected.getYear(), ( (Short) shermanObjects[ 2 ] ).shortValue() );
+				assertTrue( shermanObjects[ 3 ] instanceof Course );
+				assertEquals( courseExpected, shermanObjects[ 3 ] );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testMultiEntityProjectionAliasedList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				// should use PassThroughTransformer by default
+				return s.createCriteria( Enrolment.class, "e" )
+						.setProjection(
+								Projections.projectionList()
+										.add( Property.forName( "e.student" ).as( "st" ) )
+										.add( Property.forName( "e.semester" ).as("sem" ) )
+										.add( Property.forName( "e.year" ).as( "yr" ) )
+										.add( Property.forName( "e.course" ).as( "c" ) )
+						)
+						.addOrder( Order.asc( "e.studentNumber") );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select e.student as st, e.semester as sem, e.year as yr, e.course as c from Enrolment e order by e.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Object[] yogiObjects = ( Object[] ) resultList.get( 0 );
+				Object[] shermanObjects = ( Object[] ) resultList.get( 1 );
+				assertEquals( 4, yogiObjects.length );
+				// TODO: following is initialized for hql and uninitialied for criteria; why?
+				//assertFalse( Hibernate.isInitialized( yogiObjects[ 0 ] ) );
+				//assertFalse( Hibernate.isInitialized( shermanObjects[ 0 ] ) );
+				assertTrue( yogiObjects[ 0 ] instanceof Student );
+				assertTrue( shermanObjects[ 0 ] instanceof Student );
+				assertEquals( yogiEnrolmentExpected.getSemester(), ( (Short) yogiObjects[ 1 ] ).shortValue() );
+				assertEquals( yogiEnrolmentExpected.getYear(), ( (Short) yogiObjects[ 2 ] ).shortValue() );
+				assertEquals( courseExpected, yogiObjects[ 3 ] );
+				assertEquals( shermanEnrolmentExpected.getSemester(), ( (Short) shermanObjects[ 1 ] ).shortValue() );
+				assertEquals( shermanEnrolmentExpected.getYear(), ( (Short) shermanObjects[ 2 ] ).shortValue() );
+				assertTrue( shermanObjects[ 3 ] instanceof Course );
+				assertEquals( courseExpected, shermanObjects[ 3 ] );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testSingleAggregatedPropProjectionSingleResult() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Enrolment.class )
+						.setProjection( Projections.min( "studentNumber" ) );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select min( e.studentNumber ) from Enrolment e" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				assertTrue( results instanceof Long );
+				assertEquals( Long.valueOf( yogiExpected.getStudentNumber() ), results );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, true );
+	}
+
+	public void testMultiAggregatedPropProjectionSingleResult() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Enrolment.class )
+						.setProjection(
+								Projections.projectionList()
+									.add( Projections.min( "studentNumber" ).as( "minStudentNumber" ) )
+									.add( Projections.max( "studentNumber" ).as( "maxStudentNumber" ) )
+						);
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery(
+						"select min( e.studentNumber ) as minStudentNumber, max( e.studentNumber ) as maxStudentNumber from Enrolment e" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				assertTrue( results instanceof Object[] );
+				Object[] resultObjects = ( Object[] ) results;
+				assertEquals( Long.valueOf( yogiExpected.getStudentNumber() ), resultObjects[ 0 ] );
+				assertEquals( Long.valueOf( shermanExpected.getStudentNumber() ), resultObjects[ 1 ] );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, true );
+	}
+
+	public void testAliasToBeanDtoOneArgList() throws Exception {
+
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Enrolment.class, "e" )
+				.createAlias( "e.student", "st" )
+				.createAlias( "e.course", "co" )
+				.setProjection( Projections.property( "st.name" ).as( "studentName" ) )
+				.addOrder( Order.asc( "st.studentNumber" ) )
+				.setResultTransformer( Transformers.aliasToBean( StudentDTO.class ) );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select st.name as studentName from Student st order by st.studentNumber" )
+						.setResultTransformer( Transformers.aliasToBean( StudentDTO.class ) );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				StudentDTO dto = ( StudentDTO ) resultList.get( 0 );
+				assertNull( dto.getDescription() );
+				assertEquals( yogiExpected.getName(), dto.getName() );
+				dto = ( StudentDTO ) resultList.get( 1 );
+				assertNull( dto.getDescription() );
+				assertEquals( shermanExpected.getName(), dto.getName() );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testAliasToBeanDtoMultiArgList() throws Exception {
+
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Enrolment.class, "e" )
+				.createAlias( "e.student", "st" )
+				.createAlias( "e.course", "co" )
+				.setProjection(
+						Projections.projectionList()
+								.add( Property.forName( "st.name" ).as( "studentName" ) )
+								.add( Property.forName( "co.description" ).as( "courseDescription" ) )
+				)
+				.addOrder( Order.asc( "e.studentNumber" ) )
+				.setResultTransformer( Transformers.aliasToBean( StudentDTO.class ) );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select st.name as studentName, co.description as courseDescription from Enrolment e join e.student st join e.course co order by e.studentNumber" )
+						.setResultTransformer( Transformers.aliasToBean( StudentDTO.class ) );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				StudentDTO dto = ( StudentDTO ) resultList.get( 0 );
+				assertEquals( courseExpected.getDescription(), dto.getDescription() );
+				assertEquals( yogiExpected.getName(), dto.getName() );
+				dto = ( StudentDTO ) resultList.get( 1 );
+				assertEquals( courseExpected.getDescription(), dto.getDescription() );
+				assertEquals( shermanExpected.getName(), dto.getName() );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testMultiProjectionListThenApplyAliasToBean() throws Exception {
+
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Enrolment.class, "e" )
+				.createAlias( "e.student", "st" )
+				.createAlias( "e.course", "co" )
+				.setProjection(
+						Projections.projectionList()
+								.add( Property.forName( "st.name" ) )
+								.add( Property.forName( "co.description" ) )
+				)
+				.addOrder( Order.asc( "e.studentNumber" ) );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select st.name as studentName, co.description as courseDescription from Enrolment e join e.student st join e.course co order by e.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				ResultTransformer transformer = Transformers.aliasToBean( StudentDTO.class );
+				String[] aliases = new String[] { "studentName", "courseDescription" };
+				for ( int i = 0 ; i < resultList.size(); i++ ) {
+					resultList.set(
+							i,
+							transformer.transformTuple( ( Object[] ) resultList.get( i ), aliases )
+					);					
+				}
+
+				assertEquals( 2, resultList.size() );
+				StudentDTO dto = ( StudentDTO ) resultList.get( 0 );
+				assertEquals( courseExpected.getDescription(), dto.getDescription() );
+				assertEquals( yogiExpected.getName(), dto.getName() );
+				dto = ( StudentDTO ) resultList.get( 1 );
+				assertEquals( courseExpected.getDescription(), dto.getDescription() );
+				assertEquals( shermanExpected.getName(), dto.getName() );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testAliasToBeanDtoLiteralArgList() throws Exception {
+
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Enrolment.class, "e" )
+				.createAlias( "e.student", "st" )
+				.createAlias( "e.course", "co" )
+				.setProjection(
+						Projections.projectionList()
+								.add( Property.forName( "st.name" ).as( "studentName" ) )
+								.add( Projections.sqlProjection(
+										"'lame description' as courseDescription",
+										new String[] { "courseDescription" },
+										new Type[] { Hibernate.STRING }
+								)
+						)
+				)
+				.addOrder( Order.asc( "e.studentNumber" ) )
+				.setResultTransformer( Transformers.aliasToBean( StudentDTO.class ) );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select st.name as studentName, 'lame description' as courseDescription from Enrolment e join e.student st join e.course co order by e.studentNumber" )
+						.setResultTransformer( Transformers.aliasToBean( StudentDTO.class ) );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				StudentDTO dto = ( StudentDTO ) resultList.get( 0 );
+				assertEquals( "lame description", dto.getDescription() );
+				assertEquals( yogiExpected.getName(), dto.getName() );
+				dto = ( StudentDTO ) resultList.get( 1 );
+				assertEquals( "lame description", dto.getDescription() );
+				assertEquals( shermanExpected.getName(), dto.getName() );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testAliasToBeanDtoWithNullAliasList() throws Exception {
+
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Enrolment.class, "e" )
+				.createAlias( "e.student", "st" )
+				.createAlias( "e.course", "co" )
+				.setProjection(
+						Projections.projectionList()
+								.add( Property.forName( "st.name" ).as( "studentName" ) )
+								.add( Property.forName( "st.studentNumber" ) )
+								.add( Property.forName( "co.description" ).as( "courseDescription" ) )
+				)
+				.addOrder( Order.asc( "e.studentNumber" ) )
+				.setResultTransformer( Transformers.aliasToBean( StudentDTO.class ) );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select st.name as studentName, co.description as courseDescription from Enrolment e join e.student st join e.course co order by e.studentNumber" )
+						.setResultTransformer( Transformers.aliasToBean( StudentDTO.class ) );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				StudentDTO dto = ( StudentDTO ) resultList.get( 0 );
+				assertEquals( courseExpected.getDescription(), dto.getDescription() );
+				assertEquals( yogiExpected.getName(), dto.getName() );
+				dto = ( StudentDTO ) resultList.get( 1 );
+				assertEquals( courseExpected.getDescription(), dto.getDescription() );
+				assertEquals( shermanExpected.getName(), dto.getName() );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testOneSelectNewNoAliasesList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) throws Exception {
+				return s.createCriteria( Student.class, "s" )
+				.setProjection( Projections.property( "s.name" ) )
+				.addOrder( Order.asc( "s.studentNumber" ) )
+				.setResultTransformer( new AliasToBeanConstructorResultTransformer( getConstructor() ) );
+			}
+			private Constructor getConstructor() throws NoSuchMethodException {
+				return StudentDTO.class.getConstructor( String.class );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select new org.hibernate.test.querycache.StudentDTO(s.name) from Student s order by s.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				StudentDTO yogi = ( StudentDTO ) resultList.get( 0 );
+				assertNull( yogi.getDescription() );
+				assertEquals( yogiExpected.getName(), yogi.getName() );
+				StudentDTO sherman = ( StudentDTO ) resultList.get( 1 );
+				assertEquals( shermanExpected.getName(), sherman.getName() );
+				assertNull( sherman.getDescription() );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testOneSelectNewAliasesList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) throws Exception {
+				return s.createCriteria( Student.class, "s" )
+				.setProjection( Projections.property( "s.name" ).as( "name" ))
+				.addOrder( Order.asc( "s.studentNumber" ) )
+				.setResultTransformer( new AliasToBeanConstructorResultTransformer( getConstructor() ) );
+			}
+			private Constructor getConstructor() throws NoSuchMethodException {
+				return StudentDTO.class.getConstructor( String.class );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select new org.hibernate.test.querycache.StudentDTO(s.name) from Student s order by s.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				StudentDTO yogi = ( StudentDTO ) resultList.get( 0 );
+				assertNull( yogi.getDescription() );
+				assertEquals( yogiExpected.getName(), yogi.getName() );
+				StudentDTO sherman = ( StudentDTO ) resultList.get( 1 );
+				assertEquals( shermanExpected.getName(), sherman.getName() );
+				assertNull( sherman.getDescription() );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testMultiSelectNewList() throws Exception{
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) throws Exception {
+				return s.createCriteria( Student.class, "s" )
+				.setProjection(
+						Projections.projectionList()
+								.add( Property.forName( "s.studentNumber" ).as( "studentNumber" ))
+								.add( Property.forName( "s.name" ).as( "name" ))
+				)
+				.addOrder( Order.asc( "s.studentNumber" ) )
+				.setResultTransformer( new AliasToBeanConstructorResultTransformer( getConstructor() ) );
+			}
+			private Constructor getConstructor() throws NoSuchMethodException {
+				return  Student.class.getConstructor( long.class, String.class );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select new Student(s.studentNumber, s.name) from Student s order by s.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Student yogi = ( Student ) resultList.get( 0 );
+				assertEquals( yogiExpected.getStudentNumber(), yogi.getStudentNumber() );
+				assertEquals( yogiExpected.getName(), yogi.getName() );
+				Student sherman = ( Student ) resultList.get( 1 );
+				assertEquals( shermanExpected.getStudentNumber(), sherman.getStudentNumber() );
+				assertEquals( shermanExpected.getName(), sherman.getName() );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testMultiSelectNewWithLiteralList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) throws Exception {
+				return s.createCriteria( Student.class, "s" )
+				.setProjection(
+						Projections.projectionList()
+								.add( Projections.sqlProjection( "555 as studentNumber", new String[]{ "studentNumber" }, new Type[] { Hibernate.LONG } ) )
+								.add( Property.forName( "s.name" ).as( "name" ) )
+				)
+				.addOrder( Order.asc( "s.studentNumber" ) )
+				.setResultTransformer( new AliasToBeanConstructorResultTransformer( getConstructor() ) );
+			}
+			private Constructor getConstructor() throws NoSuchMethodException {
+				return Student.class.getConstructor( long.class, String.class );
+			}
+		};
+
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select new Student(555L, s.name) from Student s order by s.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Student yogi = ( Student ) resultList.get( 0 );
+				assertEquals( 555L, yogi.getStudentNumber() );
+				assertEquals( yogiExpected.getName(), yogi.getName() );
+				Student sherman = ( Student ) resultList.get( 1 );
+				assertEquals( 555L, sherman.getStudentNumber() );
+				assertEquals( shermanExpected.getName(), sherman.getName() );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testMultiSelectNewListList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Student.class, "s" )
+				.setProjection(
+						Projections.projectionList()
+								.add( Property.forName( "s.studentNumber" ).as( "studentNumber" ))
+								.add( Property.forName( "s.name" ).as( "name" ) )
+				)
+				.addOrder( Order.asc( "s.studentNumber" ) )
+				.setResultTransformer( Transformers.TO_LIST );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select new list(s.studentNumber, s.name) from Student s order by s.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				List yogiList = ( List ) resultList.get( 0 );
+				assertEquals( yogiExpected.getStudentNumber(), yogiList.get( 0 ) );
+				assertEquals( yogiExpected.getName(), yogiList.get( 1 ) );
+				List shermanList = ( List ) resultList.get( 1 );
+				assertEquals( shermanExpected.getStudentNumber(), shermanList.get( 0 ) );
+				assertEquals( shermanExpected.getName(), shermanList.get( 1 ) );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testMultiSelectNewMapUsingAliasesList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Student.class, "s" )
+				.setProjection(
+						Projections.projectionList()
+								.add( Property.forName( "s.studentNumber" ).as( "sNumber" ) )
+								.add( Property.forName( "s.name" ).as( "sName" ) )
+				)
+				.addOrder( Order.asc( "s.studentNumber" ) )
+				.setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select new map(s.studentNumber as sNumber, s.name as sName) from Student s order by s.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Map yogiMap = ( Map ) resultList.get( 0 );
+				assertEquals( yogiExpected.getStudentNumber(), yogiMap.get( "sNumber" ) );
+				assertEquals( yogiExpected.getName(), yogiMap.get( "sName" ) );
+				Map shermanMap = ( Map ) resultList.get( 1 );
+				assertEquals( shermanExpected.getStudentNumber(), shermanMap.get( "sNumber" ) );
+				assertEquals( shermanExpected.getName(), shermanMap.get( "sName" ) );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testMultiSelectNewMapUsingAliasesWithFetchJoinList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Student.class, "s" )
+						.createAlias( "s.preferredCourse", "pc", Criteria.LEFT_JOIN  )
+						.setFetchMode( "enrolments", FetchMode.JOIN )
+						.addOrder( Order.asc( "s.studentNumber" ))
+						.setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP );
+			}
+		};
+		HqlExecutor hqlSelectNewMapExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select new map(s as s, pc as pc) from Student s left join s.preferredCourse pc left join fetch s.enrolments order by s.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Map yogiMap = ( Map ) resultList.get( 0 );
+				assertEquals( yogiExpected, yogiMap.get( "s" ) );
+				assertEquals( yogiExpected.getPreferredCourse(), yogiMap.get( "pc" ) );
+				Map shermanMap = ( Map ) resultList.get( 1 );
+				assertEquals( shermanExpected, shermanMap.get( "s" ) );
+				assertNull( shermanMap.get( "pc" ) );
+				if ( areDynamicNonLazyAssociationsChecked() ) {
+					assertTrue( Hibernate.isInitialized( ( ( Student ) yogiMap.get( "s" ) ).getEnrolments() ) );
+					assertEquals( yogiExpected.getEnrolments(), ( ( Student ) yogiMap.get( "s" ) ).getEnrolments() );
+					assertTrue( Hibernate.isInitialized( ( ( Student ) shermanMap.get( "s" ) ).getEnrolments() ) );
+					assertEquals( shermanExpected.getEnrolments(), ( ( ( Student ) shermanMap.get( "s" ) ).getEnrolments() ) );
+				}
+			}
+		};
+		runTest( hqlSelectNewMapExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testMultiSelectAliasToEntityMapUsingAliasesWithFetchJoinList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Student.class, "s" )
+						.createAlias( "s.preferredCourse", "pc", Criteria.LEFT_JOIN  )
+						.setFetchMode( "enrolments", FetchMode.JOIN )
+						.setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP );
+			}
+		};
+		HqlExecutor hqlAliasToEntityMapExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select s as s, pc as pc from Student s left join s.preferredCourse pc left join fetch s.enrolments order by s.studentNumber" )
+						.setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Map yogiMap = ( Map ) resultList.get( 0 );
+				assertEquals( yogiExpected, yogiMap.get( "s" ) );
+				assertEquals(
+						yogiExpected.getPreferredCourse().getCourseCode(),
+						( ( Course ) yogiMap.get( "pc" ) ).getCourseCode()
+				);
+				Map shermanMap = ( Map ) resultList.get( 1 );
+				assertEquals( shermanExpected, shermanMap.get( "s" ) );
+				assertNull( shermanMap.get( "pc" ) );
+				if ( areDynamicNonLazyAssociationsChecked() ) {
+					assertEquals( yogiExpected.getPreferredCourse(), yogiMap.get( "pc" ) );
+					assertTrue( Hibernate.isInitialized( ( ( Student ) yogiMap.get( "s" ) ).getEnrolments() ) );
+					assertEquals( yogiExpected.getEnrolments(), ( ( Student ) yogiMap.get( "s" ) ).getEnrolments() );
+					assertTrue( Hibernate.isInitialized( ( ( Student ) shermanMap.get( "s" ) ).getEnrolments() ) );
+					assertEquals( shermanExpected.getEnrolments(), ( ( ( Student ) shermanMap.get( "s" ) ).getEnrolments() ) );
+				}
+			}
+		};
+		runTest( hqlAliasToEntityMapExecutor, null, checker, false );
+	}
+
+	public void testMultiSelectUsingImplicitJoinWithFetchJoinListHql() throws Exception {
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select s as s, s.preferredCourse as pc from Student s left join fetch s.enrolments" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				assertTrue( results instanceof Object[] );
+				Object[] yogiObjects = ( Object[] ) results;
+				assertEquals( 2, yogiObjects.length );
+				assertEquals( yogiExpected, yogiObjects[ 0 ] );
+				assertEquals(
+						yogiExpected.getPreferredCourse().getCourseCode(),
+						( ( Course ) yogiObjects[ 1 ] ).getCourseCode()
+				);
+				if ( areDynamicNonLazyAssociationsChecked() ) {
+					assertEquals( yogiExpected.getPreferredCourse(), yogiObjects[ 1 ] );
+					assertTrue( Hibernate.isInitialized( ( ( Student ) yogiObjects[ 0 ] ).getEnrolments() ) );
+					assertEquals( yogiExpected.getEnrolments(), ( ( Student ) yogiObjects[ 0 ] ).getEnrolments() );
+				}
+			}
+		};
+		runTest( hqlExecutor, null, checker, true );
+	}
+
+	public void testSelectNewMapUsingAliasesList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Student.class, "s" )
+				.setProjection(
+						Projections.projectionList()
+								.add( Property.forName( "s.studentNumber" ).as( "sNumber" ) )
+								.add( Property.forName( "s.name" ).as( "sName" ) )
+				)
+				.addOrder( Order.asc( "s.studentNumber" ) )
+				.setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select new map(s.studentNumber as sNumber, s.name as sName) from Student s order by s.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Map yogiMap = ( Map ) resultList.get( 0 );
+				assertEquals( yogiExpected.getStudentNumber(), yogiMap.get( "sNumber" ) );
+				assertEquals( yogiExpected.getName(), yogiMap.get( "sName" ) );
+				Map shermanMap = ( Map ) resultList.get( 1 );
+				assertEquals( shermanExpected.getStudentNumber(), shermanMap.get( "sNumber" ) );
+				assertEquals( shermanExpected.getName(), shermanMap.get( "sName" ) );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+	public void testSelectNewEntityConstructorList() throws Exception {
+		CriteriaExecutor criteriaExecutor = new CriteriaExecutor() {
+			protected Criteria getCriteria(Session s) {
+				return s.createCriteria( Student.class, "s" )
+				.setProjection(
+						Projections.projectionList()
+								.add( Property.forName( "s.studentNumber" ).as( "studentNumber" ) )
+								.add( Property.forName( "s.name" ).as( "name" ) )
+				)
+				.addOrder( Order.asc( "s.studentNumber" ) )
+				.setResultTransformer( new AliasToBeanConstructorResultTransformer( getConstructor() ) );
+			}
+			private Constructor getConstructor() {
+				Type studentNametype =
+						( ( SessionFactoryImpl ) getSessions() )
+								.getEntityPersister( Student.class.getName() )
+								.getPropertyType( "name" );
+				return ReflectHelper.getConstructor( Student.class, new Type[] { Hibernate.LONG, studentNametype } );
+			}
+		};
+		HqlExecutor hqlExecutor = new HqlExecutor() {
+			public Query getQuery(Session s) {
+				return s.createQuery( "select new Student(s.studentNumber, s.name) from Student s order by s.studentNumber" );
+			}
+		};
+		ResultChecker checker = new ResultChecker() {
+			public void check(Object results) {
+				List resultList = ( List ) results;
+				assertEquals( 2, resultList.size() );
+				Student yogi = ( Student ) resultList.get( 0 );
+				assertEquals( yogiExpected.getStudentNumber(), yogi.getStudentNumber() );
+				assertEquals( yogiExpected.getName(), yogi.getName() );
+				Student sherman = ( Student ) resultList.get( 1 );
+				assertEquals( shermanExpected.getStudentNumber(), sherman.getStudentNumber() );
+				assertEquals( shermanExpected.getName(), sherman.getName() );
+			}
+		};
+		runTest( hqlExecutor, criteriaExecutor, checker, false );
+	}
+
+
+
+	protected void runTest(HqlExecutor hqlExecutor, CriteriaExecutor criteriaExecutor, ResultChecker checker, boolean isSingleResult)
+		throws Exception {
+		createData();
+		try {
+			if ( criteriaExecutor != null ) {
+				runTest( criteriaExecutor, checker, isSingleResult );
+			}
+			if ( hqlExecutor != null ) {
+				runTest( hqlExecutor, checker, isSingleResult );
+			}
+		}
+		finally {
+			deleteData();
+		}
+	}
+
+	private boolean isQueryCacheGetEnabled() {
+		return getQueryCacheMode() == CacheMode.NORMAL ||
+			getQueryCacheMode() == CacheMode.GET;
+	}
+
+	private boolean isQueryCachePutEnabled() {
+		return getQueryCacheMode() == CacheMode.NORMAL ||
+			getQueryCacheMode() == CacheMode.PUT;
+	}
+
+	protected void runTest(QueryExecutor queryExecutor, ResultChecker resultChecker, boolean isSingleResult) throws Exception{
+		clearCache();
+		clearStatistics();
+
+		Object results = queryExecutor.execute( isSingleResult );
+
+		assertHitCount( 0 );
+		assertMissCount( isQueryCacheGetEnabled() ? 1 : 0 );
+		assertPutCount( isQueryCachePutEnabled() ? 1 : 0 );
+		clearStatistics();
+
+		resultChecker.check( results );
+
+		// check again to make sure nothing got initialized while checking results;
+		assertHitCount( 0 );
+		assertMissCount( 0 );
+		assertPutCount( 0 );
+		clearStatistics();
+
+		results = queryExecutor.execute( isSingleResult );
+
+		assertHitCount( isQueryCacheGetEnabled() ? 1 : 0 );
+		assertMissCount( 0 );
+		assertPutCount( ! isQueryCacheGetEnabled() && isQueryCachePutEnabled() ? 1 : 0 );
+		clearStatistics();
+
+		resultChecker.check( results );
+
+		// check again to make sure nothing got initialized while checking results;
+		assertHitCount( 0 );
+		assertMissCount( 0 );
+		assertPutCount( 0 );
+		clearStatistics();
+	}
+
+	private void multiPropProjectionNoTransformerDynNonLazy(CacheMode sessionCacheMode,
+																 boolean isCacheableQuery) {
+		Session s = openSession();
+		s.setCacheMode( sessionCacheMode );
+		Transaction t = s.beginTransaction();
+		List resultList = s.createCriteria( Enrolment.class )
+				.setCacheable( isCacheableQuery )
+				.setFetchMode( "student", FetchMode.JOIN )
+				.setProjection(
+						Projections.projectionList()
+								.add( Property.forName( "student" ), "student" )
+								.add( Property.forName( "semester" ), "semester" )
+								.add( Property.forName( "year" ), "year" )
+								.add( Property.forName( "course" ), "course" )
+				)
+				.addOrder( Order.asc( "studentNumber") )
+				.list();
+		t.commit();
+		s.close();
+
+		assertEquals( 2, resultList.size() );
+		Object[] yogiObjects = ( Object[] ) resultList.get( 0 );
+		Object[] shermanObjects = ( Object[] ) resultList.get( 1 );
+		assertEquals( 4, yogiObjects.length );
+		assertTrue( yogiObjects[ 0 ] instanceof Student );
+		assertTrue( Hibernate.isInitialized( yogiObjects[ 0 ] ) );
+		assertEquals( yogiEnrolmentExpected.getSemester(), ( (Short) yogiObjects[ 1 ] ).shortValue() );
+		assertEquals( yogiEnrolmentExpected.getYear(), ( (Short) yogiObjects[ 2 ] ).shortValue() );
+		assertEquals( courseExpected, yogiObjects[ 3 ] );
+		assertTrue( shermanObjects[ 0 ] instanceof Student );
+		assertTrue( Hibernate.isInitialized( shermanObjects[ 0 ] ) );
+		assertEquals( shermanEnrolmentExpected.getSemester(), ( (Short) shermanObjects[ 1 ] ).shortValue() );
+		assertEquals( shermanEnrolmentExpected.getYear(), ( (Short) shermanObjects[ 2 ] ).shortValue() );
+		assertTrue( ! ( shermanObjects[ 3 ] instanceof HibernateProxy ) );
+		assertTrue( shermanObjects[ 3 ] instanceof Course );
+		assertEquals( courseExpected, shermanObjects[ 3 ] );
+	}
+
+/*
+	{
+
+		assertEquals( 2, resultList.size() );
+		Object[] yogiObjects = ( Object[] ) resultList.get( 0 );
+		Object[] shermanObjects = ( Object[] ) resultList.get( 1 );
+		assertEquals( 4, yogiObjects.length );
+		assertEquals( yogiExpected, ( Student ) yogiObjects[ 0 ] );
+		assertEquals( yogiEnrolmentExpected.getSemester(), ( (Short) yogiObjects[ 1 ] ).shortValue() );
+		assertEquals( yogiEnrolmentExpected.getYear(), ( (Short) yogiObjects[ 2 ] ).shortValue() );
+		assertEquals( courseExpected, yogiObjects[ 3 ] );
+		assertEquals( shermanExpected, ( Student ) shermanObjects[ 0 ] );
+		assertEquals( shermanEnrolmentExpected.getSemester(), ( (Short) shermanObjects[ 1 ] ).shortValue() );
+		assertEquals( shermanEnrolmentExpected.getYear(), ( (Short) shermanObjects[ 2 ] ).shortValue() );
+		assertEquals( courseExpected, shermanObjects[ 3 ] );
+
+	}
+
+*/
+/*
+	private void executeProperty() {
+		resultList = s.createCriteria( Student.class )
+				.setCacheable( true )
+				.setProjection(
+						Projections.projectionList()
+								.add( Projections.id().as( "studentNumber" ) )
+								.add( Property.forName( "name" ), "name" )
+								.add( Property.forName( "cityState" ), "cityState" )
+								.add( Property.forName( "preferredCourse" ), "preferredCourse" )
+				)
+				.list();
+		assertEquals( 2, resultList.size() );
+		for ( Iterator it = resultList.iterator(); it.hasNext(); ) {
+			Object[] objects = ( Object[] ) it.next();
+			assertEquals( 4, objects.length );
+			assertTrue( objects[0] instanceof Long );
+			assertTrue( objects[1] instanceof String );
+			if ( "yogiExpected King".equals( objects[1] ) ) {
+				assertTrue( objects[2] instanceof Name );
+				assertTrue( objects[3] instanceof Course );
+			}
+			else {
+				assertNull( objects[2] );
+				assertNull( objects[3] );
+			}
+		}
+
+		Object[] aResult = ( Object[] ) s.createCriteria( Student.class )
+				.setCacheable( true )
+				.add( Restrictions.idEq( new Long( 667 ) ) )
+				.setProjection(
+						Projections.projectionList()
+								.add( Projections.id().as( "studentNumber" ) )
+								.add( Property.forName( "name" ), "name" )
+								.add( Property.forName( "cityState" ), "cityState" )
+								.add( Property.forName( "preferredCourse" ), "preferredCourse" )
+				)
+				.uniqueResult();
+		assertNotNull( aResult );
+		assertEquals( 4, aResult.length );
+		assertTrue( aResult[0] instanceof Long );
+		assertTrue( aResult[1] instanceof String );
+		assertTrue( aResult[2] instanceof Name );
+		assertTrue( aResult[3] instanceof Course );
+
+		Long count = ( Long ) s.createCriteria( Enrolment.class )
+				.setCacheable( true )
+				.setProjection( Property.forName( "studentNumber" ).count().setDistinct() )
+				.uniqueResult();
+		assertEquals( count, new Long( 2 ) );
+
+		Object object = s.createCriteria( Enrolment.class )
+				.setCacheable( true )
+				.setProjection(
+						Projections.projectionList()
+								.add( Property.forName( "studentNumber" ).count() )
+								.add( Property.forName( "studentNumber" ).max() )
+								.add( Property.forName( "studentNumber" ).min() )
+								.add( Property.forName( "studentNumber" ).avg() )
+				)
+				.uniqueResult();
+		Object[] result = ( Object[] ) object;
+
+		assertEquals( new Long( 2 ), result[0] );
+		assertEquals( new Long( 667 ), result[1] );
+		assertEquals( new Long( 101 ), result[2] );
+		assertEquals( 384.0, ( ( Double ) result[3] ).doubleValue(), 0.01 );
+
+
+		s.createCriteria( Enrolment.class )
+				.setCacheable( true )
+				.add( Property.forName( "studentNumber" ).gt( new Long( 665 ) ) )
+				.add( Property.forName( "studentNumber" ).lt( new Long( 668 ) ) )
+				.add( Property.forName( "courseCode" ).like( "HIB", MatchMode.START ) )
+				.add( Property.forName( "year" ).eq( new Short( ( short ) 1999 ) ) )
+				.addOrder( Property.forName( "studentNumber" ).asc() )
+				.uniqueResult();
+
+		List resultWithMaps = s.createCriteria( Enrolment.class )
+				.setCacheable( true )
+				.setProjection(
+						Projections.projectionList()
+								.add( Property.forName( "studentNumber" ).as( "stNumber" ) )
+								.add( Property.forName( "courseCode" ).as( "cCode" ) )
+				)
+				.add( Property.forName( "studentNumber" ).gt( new Long( 665 ) ) )
+				.add( Property.forName( "studentNumber" ).lt( new Long( 668 ) ) )
+				.addOrder( Property.forName( "studentNumber" ).asc() )
+				.setResultTransformer( Criteria.ALIAS_TO_ENTITY_MAP )
+				.list();
+
+		assertEquals( 1, resultWithMaps.size() );
+		Map m1 = ( Map ) resultWithMaps.get( 0 );
+
+		assertEquals( new Long( 667 ), m1.get( "stNumber" ) );
+		assertEquals( courseExpected.getCourseCode(), m1.get( "cCode" ) );
+
+		resultWithMaps = s.createCriteria( Enrolment.class )
+				.setCacheable( true )
+				.setProjection( Property.forName( "studentNumber" ).as( "stNumber" ) )
+				.addOrder( Order.desc( "stNumber" ) )
+				.setResultTransformer( Criteria.ALIAS_TO_ENTITY_MAP )
+				.list();
+
+		assertEquals( 2, resultWithMaps.size() );
+		Map m0 = ( Map ) resultWithMaps.get( 0 );
+		m1 = ( Map ) resultWithMaps.get( 1 );
+
+		assertEquals( new Long( 101 ), m1.get( "stNumber" ) );
+		assertEquals( new Long( 667 ), m0.get( "stNumber" ) );
+
+		List resultWithAliasedBean = s.createCriteria( Enrolment.class )
+				.setCacheable( true )
+				.createAlias( "student", "st" )
+				.createAlias( "courseExpected", "co" )
+				.setProjection(
+						Projections.projectionList()
+								.add( Property.forName( "st.name" ).as( "studentName" ) )
+								.add( Property.forName( "co.description" ).as( "courseDescription" ) )
+				)
+				.addOrder( Order.desc( "studentName" ) )
+				.setResultTransformer( Transformers.aliasToBean( StudentDTO.class ) )
+				.list();
+
+		assertEquals( 2, resultWithAliasedBean.size() );
+
+		StudentDTO dto = ( StudentDTO ) resultWithAliasedBean.get( 0 );
+		assertNotNull( dto.getDescription() );
+		assertNotNull( dto.getName() );
+
+		CourseMeeting courseMeetingDto = ( CourseMeeting ) s.createCriteria( CourseMeeting.class )
+				.setCacheable( true )
+				.setProjection(
+						Projections.projectionList()
+								.add( Property.forName( "id" ).as( "id" ) )
+								.add( Property.forName( "courseExpected" ).as( "courseExpected" ) )
+				)
+				.addOrder( Order.desc( "id" ) )
+				.setResultTransformer( Transformers.aliasToBean( CourseMeeting.class ) )
+				.uniqueResult();
+
+		assertNotNull( courseMeetingDto.getId() );
+		assertEquals( courseExpected.getCourseCode(), courseMeetingDto.getId().getCourseCode() );
+		assertEquals( "Monday", courseMeetingDto.getId().getDay() );
+		assertEquals( "1313 Mockingbird Lane", courseMeetingDto.getId().getLocation() );
+		assertEquals( 1, courseMeetingDto.getId().getPeriod() );
+		assertEquals( courseExpected.getDescription(), courseMeetingDto.getCourse().getDescription() );
+
+		s.createCriteria( Student.class )
+				.setCacheable( true )
+				.add( Restrictions.like( "name", "yogiExpected", MatchMode.START ) )
+				.addOrder( Order.asc( "name" ) )
+				.createCriteria( "enrolments", "e" )
+				.addOrder( Order.desc( "year" ) )
+				.addOrder( Order.desc( "semester" ) )
+				.createCriteria( "courseExpected", "c" )
+				.addOrder( Order.asc( "description" ) )
+				.setProjection(
+						Projections.projectionList()
+								.add( Property.forName( "this.name" ) )
+								.add( Property.forName( "e.year" ) )
+								.add( Property.forName( "e.semester" ) )
+								.add( Property.forName( "c.courseCode" ) )
+								.add( Property.forName( "c.description" ) )
+				)
+				.uniqueResult();
+
+		Projection p1 = Projections.projectionList()
+				.add( Property.forName( "studentNumber" ).count() )
+				.add( Property.forName( "studentNumber" ).max() )
+				.add( Projections.rowCount() );
+
+		Projection p2 = Projections.projectionList()
+				.add( Property.forName( "studentNumber" ).min() )
+				.add( Property.forName( "studentNumber" ).avg() )
+				.add(
+						Projections.sqlProjection(
+								"1 as constOne, count(*) as countStar",
+								new String[] { "constOne", "countStar" },
+								new Type[] { Hibernate.INTEGER, Hibernate.INTEGER }
+						)
+				);
+
+		Object[] array = ( Object[] ) s.createCriteria( Enrolment.class )
+				.setCacheable( true )
+				.setProjection( Projections.projectionList().add( p1 ).add( p2 ) )
+				.uniqueResult();
+
+		assertEquals( array.length, 7 );
+
+		List list = s.createCriteria( Enrolment.class )
+				.setCacheable( true )
+				.createAlias( "student", "st" )
+				.createAlias( "courseExpected", "co" )
+				.setProjection(
+						Projections.projectionList()
+								.add( Property.forName( "co.courseCode" ).group() )
+								.add( Property.forName( "st.studentNumber" ).count().setDistinct() )
+								.add( Property.forName( "year" ).group() )
+				)
+				.list();
+
+		assertEquals( list.size(), 2 );
+	}
+*/
+	protected void clearCache() {
+		getSessions().evictQueries();
+	}
+
+	protected void clearStatistics() {
+		getSessions().getStatistics().clear();
+	}
+
+	protected void assertEntityFetchCount(int expected) {
+		int actual = ( int ) getSessions().getStatistics().getEntityFetchCount();
+		assertEquals( expected, actual );
+	}
+
+	protected void assertCount(int expected) {
+		int actual = ( int ) getSessions().getStatistics().getQueries().length;
+		assertEquals( expected, actual );
+	}
+
+	protected void assertHitCount(int expected) {
+		int actual = ( int ) getSessions().getStatistics().getQueryCacheHitCount();
+		assertEquals( expected, actual );
+	}
+
+	protected void assertMissCount(int expected) {
+		int actual = ( int ) getSessions().getStatistics().getQueryCacheMissCount();
+		assertEquals( expected, actual );
+	}
+
+	protected void assertPutCount(int expected) {
+		int actual = ( int ) getSessions().getStatistics().getQueryCachePutCount();
+		assertEquals( expected, actual );
+	}
+
+	protected void assertInsertCount(int expected) {
+		int inserts = ( int ) getSessions().getStatistics().getEntityInsertCount();
+		assertEquals( "unexpected insert count", expected, inserts );
+	}
+
+	protected void assertUpdateCount(int expected) {
+		int updates = ( int ) getSessions().getStatistics().getEntityUpdateCount();
+		assertEquals( "unexpected update counts", expected, updates );
+	}
+
+	protected void assertDeleteCount(int expected) {
+		int deletes = ( int ) getSessions().getStatistics().getEntityDeleteCount();
+		assertEquals( "unexpected delete counts", expected, deletes );
+	}
+}

Added: core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/Address.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/Address.java	                        (rev 0)
+++ core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/Address.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -0,0 +1,134 @@
+// $Id: Address.java 7996 2005-08-22 14:49:57Z steveebersole $
+package org.hibernate.test.querycache;
+
+/**
+ * Implementation of Address.
+ *
+ * @author Steve Ebersole
+ */
+public class Address {
+	private long id;
+	private String addressType;
+	private String street;
+	private String city;
+	private String stateProvince;
+	private String postalCode;
+	private String country;
+	private Student student;
+
+	public Address() {}
+
+	public Address(Student student, String type, String street, String city, String stateProvince, String postalCode, String country) {
+		this.student = student;
+		this.addressType = type;
+		this.street = street;
+		this.city = city;
+		this.stateProvince = stateProvince;
+		this.postalCode = postalCode;
+		this.country = country;
+	}
+
+	public long getId() {
+		return id;
+	}
+
+	public void setId(long id) {
+		this.id = id;
+	}
+
+	public Student getStudent() {
+		return student;
+	}
+
+	public void setStudent(Student student) {
+		this.student = student;
+	}
+
+	public String getAddressType() {
+		return addressType;
+	}
+
+	public void setAddressType(String addressType) {
+		this.addressType = addressType;
+	}
+
+	public String getStreet() {
+		return street;
+	}
+
+	public void setStreet(String street) {
+		this.street = street;
+	}
+
+	public String getCity() {
+		return city;
+	}
+
+	public void setCity(String city) {
+		this.city = city;
+	}
+
+	public String getPostalCode() {
+		return postalCode;
+	}
+
+	public void setPostalCode(String postalCode) {
+		this.postalCode = postalCode;
+	}
+
+	public String getCountry() {
+		return country;
+	}
+
+	public void setCountry(String country) {
+		this.country = country;
+	}
+
+	public String getStateProvince() {
+		return stateProvince;
+	}
+
+	public void setStateProvince(String stateProvince) {
+		this.stateProvince = stateProvince;
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if ( this == o ) {
+			return true;
+		}
+		if ( o == null || getClass() != o.getClass() ) {
+			return false;
+		}
+
+		Address address = ( Address ) o;
+
+		if ( city != null ? !city.equals( address.city ) : address.city != null ) {
+			return false;
+		}
+		if ( country != null ? !country.equals( address.country ) : address.country != null ) {
+			return false;
+		}
+		if ( postalCode != null ? !postalCode.equals( address.postalCode ) : address.postalCode != null ) {
+			return false;
+		}
+		if ( stateProvince != null ? !stateProvince.equals( address.stateProvince ) : address.stateProvince != null ) {
+			return false;
+		}
+		if ( street != null ? !street.equals( address.street ) : address.street != null ) {
+			return false;
+		}
+
+		return true;
+	}
+
+	@Override
+	public int hashCode() {
+		int result = street != null ? street.hashCode() : 0;
+		result = 31 * result + ( city != null ? city.hashCode() : 0 );
+		result = 31 * result + ( stateProvince != null ? stateProvince.hashCode() : 0 );
+		result = 31 * result + ( postalCode != null ? postalCode.hashCode() : 0 );
+		result = 31 * result + ( country != null ? country.hashCode() : 0 );
+		return result;
+	}
+}
\ No newline at end of file

Added: core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/Course.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/Course.java	                        (rev 0)
+++ core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/Course.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -0,0 +1,60 @@
+//$Id: Course.java 5686 2005-02-12 07:27:32Z steveebersole $
+package org.hibernate.test.querycache;
+
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Gavin King
+ */
+public class Course implements Serializable {
+	private String courseCode;
+	private String description;
+	private Set courseMeetings = new HashSet();
+
+	public String getCourseCode() {
+		return courseCode;
+	}
+	public void setCourseCode(String courseCode) {
+		this.courseCode = courseCode;
+	}
+	public String getDescription() {
+		return description;
+	}
+	public void setDescription(String description) {
+		this.description = description;
+	}
+	public Set getCourseMeetings() {
+		return courseMeetings;
+	}
+	public void setCourseMeetings(Set courseMeetings) {
+		this.courseMeetings = courseMeetings;
+	}
+
+	public boolean equals(Object o) {
+		if ( this == o ) {
+			return true;
+		}
+		if ( o == null || ! ( o instanceof Course ) ) {
+			return false;
+		}
+
+		Course course = ( Course ) o;
+
+		if ( courseCode != null ? !courseCode.equals( course.getCourseCode() ) : course.getCourseCode() != null ) {
+			return false;
+		}
+		if ( description != null ? !description.equals( course.getDescription() ) : course.getDescription() != null ) {
+			return false;
+		}
+
+		return true;
+	}
+
+	public int hashCode() {
+		int result = courseCode != null ? courseCode.hashCode() : 0;
+		result = 31 * result + ( description != null ? description.hashCode() : 0 );
+		return result;
+	}
+}
\ No newline at end of file

Added: core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/CourseMeeting.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/CourseMeeting.java	                        (rev 0)
+++ core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/CourseMeeting.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -0,0 +1,55 @@
+package org.hibernate.test.querycache;
+
+/**
+ * @author Gail Badner
+ */
+public class CourseMeeting {
+	private CourseMeetingId id;
+	private Course course;
+
+	public CourseMeeting() {}
+
+	public CourseMeeting(Course course, String day, int period, String location) {
+		this.id = new CourseMeetingId( course, day, period, location );
+		this.course = course;
+	}
+
+	public CourseMeetingId getId() {
+		return id;
+	}
+	public void setId(CourseMeetingId id) {
+		this.id = id;
+	}
+	public  Course getCourse() {
+		return course;
+	}
+	public void setCourse(Course course) {
+		this.course = course;
+	}
+
+	public boolean equals(Object o) {
+		if ( this == o ) {
+			return true;
+		}
+		if ( o == null || getClass() != o.getClass() ) {
+			return false;
+		}
+
+		CourseMeeting that = ( CourseMeeting ) o;
+
+		if ( course != null ? !course.equals( that.course ) : that.course != null ) {
+			return false;
+		}
+		if ( id != null ? !id.equals( that.id ) : that.id != null ) {
+			return false;
+		}
+
+		return true;
+	}
+
+	public int hashCode() {
+		int result = id != null ? id.hashCode() : 0;
+		result = 31 * result + ( course != null ? course.hashCode() : 0 );
+		return result;
+	}
+}
\ No newline at end of file

Added: core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/CourseMeetingId.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/CourseMeetingId.java	                        (rev 0)
+++ core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/CourseMeetingId.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -0,0 +1,81 @@
+package org.hibernate.test.querycache;
+
+import java.io.Serializable;
+
+/**
+ * @author Gail Badner
+ */
+public class CourseMeetingId implements Serializable {
+	private String courseCode;
+	private String day;
+	private int period;
+	private String location;
+
+	public CourseMeetingId() {}
+
+	public CourseMeetingId(Course course, String day, int period, String location) {
+		this.courseCode = course.getCourseCode();
+		this.day = day;
+		this.period = period;
+		this.location = location;
+	}
+
+	public String getCourseCode() {
+		return courseCode;
+	}
+	public void setCourseCode(String courseCode) {
+		this.courseCode = courseCode;
+	}
+	public String getDay() {
+		return day;
+	}
+	public void setDay(String day) {
+		this.day = day;
+	}
+	public int getPeriod() {
+		return period;
+	}
+	public void setPeriod(int period) {
+		this.period = period;
+	}
+	public String getLocation() {
+		return location;
+	}
+	public void setLocation(String location) {
+		this.location = location;
+	}
+
+	public boolean equals(Object o) {
+		if ( this == o ) {
+			return true;
+		}
+		if ( o == null || getClass() != o.getClass() ) {
+			return false;
+		}
+
+		CourseMeetingId that = ( CourseMeetingId ) o;
+
+		if ( period != that.period ) {
+			return false;
+		}
+		if ( courseCode != null ? !courseCode.equals( that.courseCode ) : that.courseCode != null ) {
+			return false;
+		}
+		if ( day != null ? !day.equals( that.day ) : that.day != null ) {
+			return false;
+		}
+		if ( location != null ? !location.equals( that.location ) : that.location != null ) {
+			return false;
+		}
+
+		return true;
+	}
+
+	public int hashCode() {
+		int result = courseCode != null ? courseCode.hashCode() : 0;
+		result = 31 * result + ( day != null ? day.hashCode() : 0 );
+		result = 31 * result + period;
+		result = 31 * result + ( location != null ? location.hashCode() : 0 );
+		return result;
+	}
+}
\ No newline at end of file

Added: core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/CriteriaQueryCacheIgnoreResultTransformerTest.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/CriteriaQueryCacheIgnoreResultTransformerTest.java	                        (rev 0)
+++ core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/CriteriaQueryCacheIgnoreResultTransformerTest.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -0,0 +1,61 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ *
+ */
+package org.hibernate.test.querycache;
+
+import junit.framework.Test;
+
+import org.hibernate.CacheMode;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gail Badner
+ */
+public class CriteriaQueryCacheIgnoreResultTransformerTest extends AbstractQueryCacheResultTransformerTest {
+
+	public CriteriaQueryCacheIgnoreResultTransformerTest(String str) {
+		super( str );
+	}
+
+	public static Test suite() {
+		return new FunctionalTestClassTestSuite( CriteriaQueryCacheIgnoreResultTransformerTest.class );
+	}
+
+	protected CacheMode getQueryCacheMode() {
+		return CacheMode.IGNORE;
+	}
+
+	protected void runTest(HqlExecutor hqlExecutor, CriteriaExecutor criteriaExecutor, ResultChecker checker, boolean isSingleResult)
+		throws Exception {
+		createData();
+		try {
+			if ( criteriaExecutor != null ) {
+				runTest( criteriaExecutor, checker, isSingleResult );
+			}
+		}
+		finally {
+			deleteData();
+		}
+	}
+}

Added: core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/CriteriaQueryCacheNormalResultTransformerTest.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/CriteriaQueryCacheNormalResultTransformerTest.java	                        (rev 0)
+++ core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/CriteriaQueryCacheNormalResultTransformerTest.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -0,0 +1,48 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ *
+ */
+package org.hibernate.test.querycache;
+
+import junit.framework.Test;
+
+import org.hibernate.CacheMode;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gail Badner
+ */
+public class CriteriaQueryCacheNormalResultTransformerTest extends CriteriaQueryCachePutResultTransformerTest {
+
+	public CriteriaQueryCacheNormalResultTransformerTest(String str) {
+		super( str );
+	}
+
+	public static Test suite() {
+		return new FunctionalTestClassTestSuite( CriteriaQueryCacheNormalResultTransformerTest.class );
+	}
+
+	protected CacheMode getQueryCacheMode() {
+		return CacheMode.NORMAL;
+	}
+}

Added: core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/CriteriaQueryCachePutResultTransformerTest.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/CriteriaQueryCachePutResultTransformerTest.java	                        (rev 0)
+++ core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/CriteriaQueryCachePutResultTransformerTest.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -0,0 +1,52 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ *
+ */
+package org.hibernate.test.querycache;
+
+import junit.framework.Test;
+
+import org.hibernate.CacheMode;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gail Badner
+ */
+public class CriteriaQueryCachePutResultTransformerTest extends CriteriaQueryCacheIgnoreResultTransformerTest {
+
+	public CriteriaQueryCachePutResultTransformerTest(String str) {
+		super( str );
+	}
+
+	public static Test suite() {
+		return new FunctionalTestClassTestSuite( CriteriaQueryCachePutResultTransformerTest.class );
+	}
+
+	protected CacheMode getQueryCacheMode() {
+		return CacheMode.PUT;
+	}
+
+	protected boolean areDynamicNonLazyAssociationsChecked() {
+		return false;
+	}
+}

Added: core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/Enrolment.hbm.xml
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/Enrolment.hbm.xml	                        (rev 0)
+++ core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/Enrolment.hbm.xml	2010-11-29 07:23:24 UTC (rev 20872)
@@ -0,0 +1,91 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC 
+	"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.querycache">
+	
+	<class name="Course">
+		<id name="courseCode">
+			<generator class="assigned"/>
+		</id>
+		<property name="description"/>
+        <set name="courseMeetings" inverse="true" cascade="all-delete-orphan" lazy="false">
+            <key column="courseCode"/>
+            <one-to-many class="CourseMeeting"/>
+        </set>
+	</class>
+
+    <class name="CourseMeeting">
+        <composite-id name="id" class="CourseMeetingId">
+            <key-property name="courseCode"/>
+            <key-property name="day"/>
+            <key-property name="period"/>
+            <key-property name="location"/>
+        </composite-id>
+        <many-to-one name="course" insert="false" update="false" lazy="false">
+            <column name="courseCode"/>
+        </many-to-one>
+    </class>
+
+	<class name="Student">
+		<id name="studentNumber">
+		    <column name="studentId"/>
+			<generator class="assigned"/>
+		</id>
+		<property name="name"></property>
+        <!--<component name="name">
+            <property name="first" column="name_first" not-null="true"/>
+            <property name="middle" column="name_middle" not-null="false"/>
+            <property name="last" column="name_last" not-null="true"/>
+        </component>
+		--><set name="enrolments" inverse="true" cascade="delete">
+			<key column="studentId"/>
+			<one-to-many class="Enrolment"/>
+		</set>
+        <map name="addresses" table="addresses" cascade="all,delete" lazy="true">
+            <key column="studentNumber"/>
+            <map-key column="addressType" type="string"/>
+            <one-to-many class="Address"/>
+        </map>
+        <many-to-one name="preferredCourse" column="preferredCourseCode" lazy="proxy"/>
+        <list name="secretCodes" lazy="false">
+            <key>
+                <column name="studentNumber"/>
+            </key>
+            <index column="i"/>
+            <element column="secretCode" type="int"/>
+        </list>
+	</class>
+
+    <class name="Address">
+        <id name="id">
+			<generator class="increment"/>
+		</id>
+        <property name="addressType"/>
+        <property name="street"/>
+        <property name="city"/>
+        <property name="stateProvince"/>
+        <property name="postalCode"/>
+        <property name="country"/>
+        <many-to-one name="student" class="Student" column="studentNumber" not-null="false"/>
+    </class>
+
+	<class name="Enrolment">
+		<composite-id>
+			<key-property name="studentNumber">
+				<column name="studentId"/>
+			</key-property>
+			<key-property name="courseCode"/>
+		</composite-id>
+		<many-to-one name="student" insert="false" update="false" lazy="proxy">
+			<column name="studentId"/>
+		</many-to-one>
+		<many-to-one name="course" insert="false" update="false" lazy="false">
+			<column name="courseCode"/>
+		</many-to-one>
+		<property name="semester" type="short" not-null="true"/>
+		<property name="year" column="`year`" type="short" not-null="true"/>
+	</class>
+
+</hibernate-mapping>

Added: core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/Enrolment.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/Enrolment.java	                        (rev 0)
+++ core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/Enrolment.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -0,0 +1,87 @@
+//$Id: Enrolment.java 6970 2005-05-31 20:24:41Z oneovthafew $
+package org.hibernate.test.querycache;
+
+import java.io.Serializable;
+
+/**
+ * @author Gavin King
+ */
+public class Enrolment implements Serializable {
+	private Student student;
+	private Course course;
+	private long studentNumber;
+	private String courseCode;
+	private short year;
+	private short semester;
+
+	public String getCourseCode() {
+		return courseCode;
+	}
+	public void setCourseCode(String courseId) {
+		this.courseCode = courseId;
+	}
+	public long getStudentNumber() {
+		return studentNumber;
+	}
+	public void setStudentNumber(long studentId) {
+		this.studentNumber = studentId;
+	}
+	public Course getCourse() {
+		return course;
+	}
+	public void setCourse(Course course) {
+		this.course = course;
+	}
+	public Student getStudent() {
+		return student;
+	}
+	public void setStudent(Student student) {
+		this.student = student;
+	}
+	public short getSemester() {
+		return semester;
+	}
+	public void setSemester(short semester) {
+		this.semester = semester;
+	}
+	public short getYear() {
+		return year;
+	}
+	public void setYear(short year) {
+		this.year = year;
+	}
+
+	public boolean equals(Object o) {
+		if ( this == o ) {
+			return true;
+		}
+		if ( o == null || getClass() != o.getClass() ) {
+			return false;
+		}
+
+		Enrolment enrolment = ( Enrolment ) o;
+
+		if ( semester != enrolment.semester ) {
+			return false;
+		}
+		if ( studentNumber != enrolment.studentNumber ) {
+			return false;
+		}
+		if ( year != enrolment.year ) {
+			return false;
+		}
+		if ( courseCode != null ? !courseCode.equals( enrolment.courseCode ) : enrolment.courseCode != null ) {
+			return false;
+		}
+
+		return true;
+	}
+
+	public int hashCode() {
+		int result = ( int ) ( studentNumber ^ ( studentNumber >>> 32 ) );
+		result = 31 * result + ( courseCode != null ? courseCode.hashCode() : 0 );
+		result = 31 * result + ( int ) year;
+		result = 31 * result + ( int ) semester;
+		return result;
+	}
+}
\ No newline at end of file

Added: core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/HqlQueryCacheIgnoreResultTransformerTest.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/HqlQueryCacheIgnoreResultTransformerTest.java	                        (rev 0)
+++ core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/HqlQueryCacheIgnoreResultTransformerTest.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -0,0 +1,87 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ *
+ */
+package org.hibernate.test.querycache;
+
+import junit.framework.Test;
+
+import org.hibernate.CacheMode;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gail Badner
+ */
+public class HqlQueryCacheIgnoreResultTransformerTest extends AbstractQueryCacheResultTransformerTest {
+
+	public HqlQueryCacheIgnoreResultTransformerTest(String str) {
+		super( str );
+	}
+
+	public static Test suite() {
+		return new FunctionalTestClassTestSuite( HqlQueryCacheIgnoreResultTransformerTest.class );
+	}
+
+	protected CacheMode getQueryCacheMode() {
+		return CacheMode.IGNORE;
+	}
+
+	protected void runTest(HqlExecutor hqlExecutor, CriteriaExecutor criteriaExecutor, ResultChecker checker, boolean isSingleResult)
+		throws Exception {
+		createData();
+		if ( hqlExecutor != null ) {
+			runTest( hqlExecutor, checker, isSingleResult );
+		}
+		deleteData();
+	}
+
+	public void testAliasToEntityMapNoProjectionList() throws Exception {
+		reportSkip( "known to fail using HQL", "HQL query using Transformers.ALIAS_TO_ENTITY_MAP with no projection" );
+	}
+	public void testAliasToEntityMapNoProjectionListFailureExpected() throws Exception {
+		super.testAliasToEntityMapNoProjectionList();
+	}
+
+	public void testAliasToEntityMapNoProjectionMultiAndNullList() throws Exception {
+		reportSkip( "known to fail using HQL", "HQL query using Transformers.ALIAS_TO_ENTITY_MAP with no projection" );
+	}
+	public void testAliasToEntityMapNoProjectionMultiAndNullListFailureExpected() throws Exception {
+		super.testAliasToEntityMapNoProjectionMultiAndNullList();
+	}
+
+	public void testAliasToEntityMapNoProjectionNullAndNonNullAliasList() throws Exception {
+		reportSkip( "known to fail using HQL", "HQL query using Transformers.ALIAS_TO_ENTITY_MAP with no projection" );
+	}
+	public void testAliasToEntityMapNoProjectionNullAndNonNullAliasListFailureExpected() throws Exception {
+		super.testAliasToEntityMapNoProjectionNullAndNonNullAliasList();
+	}
+
+	// fails due to HHH-3345
+	public void testMultiSelectNewMapUsingAliasesWithFetchJoinList() throws Exception {
+		reportSkip( "known to fail using HQL", "HQL query using 'select new' and 'join fetch'" );
+	}
+	public void testMultiSelectNewMapUsingAliasesWithFetchJoinListFailureExpected() throws Exception {
+		super.testMultiSelectNewMapUsingAliasesWithFetchJoinList();
+	}
+
+}

Added: core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/HqlQueryCacheNormalResultTransformerTest.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/HqlQueryCacheNormalResultTransformerTest.java	                        (rev 0)
+++ core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/HqlQueryCacheNormalResultTransformerTest.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -0,0 +1,48 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ *
+ */
+package org.hibernate.test.querycache;
+
+import junit.framework.Test;
+
+import org.hibernate.CacheMode;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gail Badner
+ */
+public class HqlQueryCacheNormalResultTransformerTest extends HqlQueryCachePutResultTransformerTest {
+
+	public HqlQueryCacheNormalResultTransformerTest(String str) {
+		super( str );
+	}
+
+	public static Test suite() {
+		return new FunctionalTestClassTestSuite( HqlQueryCacheNormalResultTransformerTest.class );
+	}
+
+	protected CacheMode getQueryCacheMode() {
+		return CacheMode.NORMAL;
+	}
+}

Added: core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/HqlQueryCachePutResultTransformerTest.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/HqlQueryCachePutResultTransformerTest.java	                        (rev 0)
+++ core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/HqlQueryCachePutResultTransformerTest.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -0,0 +1,53 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ *
+ */
+package org.hibernate.test.querycache;
+
+import junit.framework.Test;
+
+import org.hibernate.CacheMode;
+import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
+
+/**
+ * @author Gail Badner
+ */
+public class HqlQueryCachePutResultTransformerTest extends HqlQueryCacheIgnoreResultTransformerTest {
+
+	public HqlQueryCachePutResultTransformerTest(String str) {
+		super( str );
+	}
+
+	public static Test suite() {
+		return new FunctionalTestClassTestSuite( HqlQueryCachePutResultTransformerTest.class );
+	}
+
+
+	protected CacheMode getQueryCacheMode() {
+		return CacheMode.PUT;
+	}
+
+	protected boolean areDynamicNonLazyAssociationsChecked() {
+		return false;
+	}
+}

Modified: core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/QueryCacheTest.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/QueryCacheTest.java	2010-11-17 10:24:10 UTC (rev 20871)
+++ core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/QueryCacheTest.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -42,6 +42,51 @@
 	public static Test suite() {
 		return new FunctionalTestClassTestSuite( QueryCacheTest.class );
 	}
+	
+	public void testInvalidationFromBulkHQL() {
+		// http://opensource.atlassian.com/projects/hibernate/browse/HHH-5426
+
+		getSessions().evictQueries();
+		getSessions().getStatistics().clear();
+
+		Session s = openSession();
+		List list = new ArrayList();
+		s.beginTransaction();
+		for (int i = 0; i < 3; i++) {
+			Item a = new Item();
+			a.setName("a" + i);
+			a.setDescription("a" + i);
+			list.add(a);
+			s.persist(a);
+		}
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		String queryString = "select count(*) from Item";
+		// this query will hit the database and create the cache
+		Long result = (Long) s.createQuery(queryString).setCacheable(true).uniqueResult();
+		assertEquals(3, result.intValue());
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		String updateString = "delete from Item";
+		s.createQuery(updateString).executeUpdate();
+		s.getTransaction().commit();
+		s.close();
+
+		s = openSession();
+		s.beginTransaction();
+		// and this one SHOULD not be served by the cache
+		Number result2 = (Number) s.createQuery(queryString).setCacheable(true).uniqueResult();
+		assertEquals(0, result2.intValue());
+		s.getTransaction().commit();
+		s.close();
+	}
+	
 	//https://jira.jboss.org/jira/browse/JBPAPP-4224
 	public void testHitCacheInSameSession() {
 		getSessions().evictQueries();
@@ -224,7 +269,7 @@
 		getSessions().evictQueries();
         getSessions().getStatistics().clear();
 
-		final String queryString = "select i.description from Item i where i.name='widget'";
+		final String queryString = "select i.description as desc from Item i where i.name='widget'";
 
 		Session s = openSession();
 		Transaction t = s.beginTransaction();
@@ -239,25 +284,34 @@
         QueryStatistics qs = s.getSessionFactory().getStatistics().getQueryStatistics( queryString );
 		EntityStatistics es = s.getSessionFactory().getStatistics().getEntityStatistics( Item.class.getName() );
 
+		assertEquals( qs.getCacheHitCount(), 0 );
+		assertEquals( qs.getCacheMissCount(), 1 );
+		assertEquals( qs.getCachePutCount(), 1 );
 		Thread.sleep(200);
 
 		s = openSession();
 		t = s.beginTransaction();
 		List result = s.createQuery( queryString ).setCacheable(true).list();
 		assertEquals( result.size(), 1 );
+		assertEquals( i.getDescription(), ( ( String ) result.get( 0 ) ) );
 		t.commit();
 		s.close();
 
 		assertEquals( qs.getCacheHitCount(), 0 );
+		assertEquals( qs.getCacheMissCount(), 2 );
+		assertEquals( qs.getCachePutCount(), 2 );
 
 		s = openSession();
 		t = s.beginTransaction();
 		result = s.createQuery( queryString ).setCacheable(true).list();
 		assertEquals( result.size(), 1 );
+		assertEquals( i.getDescription(), result.get( 0 ) );
 		t.commit();
 		s.close();
 
 		assertEquals( qs.getCacheHitCount(), 1 );
+		assertEquals( qs.getCacheMissCount(), 2 );
+		assertEquals( qs.getCachePutCount(), 2 );
 
 		s = openSession();
 		t = s.beginTransaction();
@@ -265,10 +319,13 @@
 		assertEquals( result.size(), 1 );
 		Map m = (Map) result.get(0);
 		assertEquals(1, m.size());
+		assertEquals( i.getDescription(), m.get( "desc" ) );
 		t.commit();
 		s.close();
 
-		assertEquals( "hit count should not go up since we are adding a resulttransformer", qs.getCacheHitCount(), 1 );
+		assertEquals( "hit count should go up since data is not transformed until after it is cached", qs.getCacheHitCount(), 2 );
+		assertEquals( qs.getCacheMissCount(), 2 );
+		assertEquals( qs.getCachePutCount(), 2 );
 		
 		s = openSession();
 		t = s.beginTransaction();
@@ -276,10 +333,13 @@
 		assertEquals( result.size(), 1 );
 		m = (Map) result.get(0);
 		assertEquals(1, m.size());
+		assertEquals( i.getDescription(), m.get( "desc" ) );
 		t.commit();
 		s.close();
 		
-		assertEquals( "hit count should go up since we are using the same resulttransformer", qs.getCacheHitCount(), 2 );
+		assertEquals( "hit count should go up since data is not transformed until after it is cachedr", qs.getCacheHitCount(), 3 );
+		assertEquals( qs.getCacheMissCount(), 2 );
+		assertEquals( qs.getCachePutCount(), 2 );
 		
 		s = openSession();
 		t = s.beginTransaction();
@@ -292,8 +352,9 @@
 		t.commit();
 		s.close();
 
-		assertEquals( qs.getCacheHitCount(), 3 );
-		assertEquals( qs.getCacheMissCount(), 3 );
+		assertEquals( qs.getCacheHitCount(), 4 );
+		assertEquals( qs.getCacheMissCount(), 2 );
+		assertEquals( qs.getCachePutCount(), 2 );
 
 		Thread.sleep(200);
 
@@ -304,14 +365,18 @@
 		i = (Item) s.get( Item.class, new Long(i.getId()) );
 		assertEquals( (String) result.get(0), "A middle-quality widget." );
 		
+		assertEquals( qs.getCacheHitCount(), 4 );
+		assertEquals( qs.getCacheMissCount(), 3 );
+		assertEquals( qs.getCachePutCount(), 3 );
+		
 		s.delete(i);
 		t.commit();
 		s.close();
 
-		assertEquals( qs.getCacheHitCount(), 3 );
-		assertEquals( qs.getCacheMissCount(), 4 );
-		assertEquals( qs.getCachePutCount(), 4 );
-		assertEquals( qs.getExecutionCount(), 4 );
+		assertEquals( qs.getCacheHitCount(), 4 );
+		assertEquals( qs.getCacheMissCount(), 3 );
+		assertEquals( qs.getCachePutCount(), 3 );
+		assertEquals( qs.getExecutionCount(), 3 );
 		assertEquals( es.getFetchCount(), 0 ); //check that it was being cached
 
 	}

Added: core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/Student.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/Student.java	                        (rev 0)
+++ core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/Student.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -0,0 +1,102 @@
+//$Id: Student.java 9116 2006-01-23 21:21:01Z steveebersole $
+package org.hibernate.test.querycache;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Gavin King
+ */
+public class Student {
+	private long studentNumber;
+	private String name;
+	private Course preferredCourse;
+	private Set enrolments = new HashSet();
+	private Map addresses = new HashMap();
+	private List secretCodes = new ArrayList();
+
+	public Student() {}
+
+	public Student(long studentNumber, String name) {
+		this.studentNumber = studentNumber;
+		this.name = name;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public long getStudentNumber() {
+		return studentNumber;
+	}
+
+	public void setStudentNumber(long studentNumber) {
+		this.studentNumber = studentNumber;
+	}
+
+	public Map getAddresses() {
+		return addresses;
+	}
+
+	public void setAddresses(Map addresses) {
+		this.addresses = addresses;
+	}
+
+	public Course getPreferredCourse() {
+		return preferredCourse;
+	}
+
+	public void setPreferredCourse(Course preferredCourse) {
+		this.preferredCourse = preferredCourse;
+	}
+
+	public Set getEnrolments() {
+		return enrolments;
+	}
+
+	public void setEnrolments(Set employments) {
+		this.enrolments = employments;
+	}
+
+	public List getSecretCodes() {
+		return secretCodes;
+	}
+
+	public void setSecretCodes(List secretCodes) {
+		this.secretCodes = secretCodes;
+	}
+
+	public boolean equals(Object o) {
+		if ( this == o ) {
+			return true;
+		}
+		if ( o == null || ! ( o instanceof Student ) ) {
+			return false;
+		}
+
+		Student student = ( Student ) o;
+
+		if ( studentNumber != student.getStudentNumber() ) {
+			return false;
+		}
+		if ( name != null ? !name.equals( student.getName() ) : student.getName() != null ) {
+			return false;
+		}
+
+		return true;
+	}
+
+	public int hashCode() {
+		int result = ( int ) ( studentNumber ^ ( studentNumber >>> 32 ) );
+		result = 31 * result + ( name != null ? name.hashCode() : 0 );
+		return result;
+	}
+}
\ No newline at end of file

Added: core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/StudentDTO.java
===================================================================
--- core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/StudentDTO.java	                        (rev 0)
+++ core/branches/Branch_3_3_2_GA_CP/testsuite/src/test/java/org/hibernate/test/querycache/StudentDTO.java	2010-11-29 07:23:24 UTC (rev 20872)
@@ -0,0 +1,60 @@
+/*
+ * Created on 28-Jan-2005
+ *
+ */
+package org.hibernate.test.querycache;
+
+/**
+ * @author max
+ *
+ */
+public class StudentDTO {
+
+	private String studentName;
+	private String courseDescription;
+
+	public StudentDTO() { }
+
+	public StudentDTO(String name) {
+		this.studentName = name;
+	}
+
+	public StudentDTO(String name, String description) {
+		this.studentName = name;
+		this.courseDescription = description;
+	}
+
+	public String getName() {
+		return studentName;
+	}
+
+	public String getDescription() {
+		return courseDescription;
+	}
+
+	public boolean equals(Object o) {
+		if ( this == o ) {
+			return true;
+		}
+		if ( o == null || getClass() != o.getClass() ) {
+			return false;
+		}
+
+		StudentDTO that = ( StudentDTO ) o;
+
+		if ( courseDescription != null ? !courseDescription.equals( that.courseDescription ) : that.courseDescription != null ) {
+			return false;
+		}
+		if ( studentName != null ? !studentName.equals( that.studentName ) : that.studentName != null ) {
+			return false;
+		}
+
+		return true;
+	}
+
+	public int hashCode() {
+		int result = studentName != null ? studentName.hashCode() : 0;
+		result = 31 * result + ( courseDescription != null ? courseDescription.hashCode() : 0 );
+		return result;
+	}
+}
\ No newline at end of file



More information about the hibernate-commits mailing list