diff --git a/core/src/main/java/org/hibernate/cache/QueryKey.java b/core/src/main/java/org/hibernate/cache/QueryKey.java index e66271f420..6dfa6125df 100644 --- a/core/src/main/java/org/hibernate/cache/QueryKey.java +++ b/core/src/main/java/org/hibernate/cache/QueryKey.java @@ -34,6 +34,7 @@ import org.hibernate.engine.QueryParameters; import org.hibernate.engine.RowSelection; import org.hibernate.engine.SessionImplementor; import org.hibernate.engine.TypedValue; +import org.hibernate.transform.CacheableResultTransformer; import org.hibernate.transform.ResultTransformer; import org.hibernate.type.Type; import org.hibernate.util.EqualsHelper; @@ -58,7 +59,7 @@ public class QueryKey implements Serializable { // 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 @@ -83,7 +84,7 @@ public class QueryKey implements Serializable { QueryParameters queryParameters, Set filterKeys, SessionImplementor session, - ResultTransformer customTransformer) { + CacheableResultTransformer customTransformer) { // disassemble positional parameters final int positionalParameterCount = queryParameters.getPositionalParameterTypes().length; final Type[] types = new Type[positionalParameterCount]; @@ -163,7 +164,7 @@ public class QueryKey implements Serializable { Integer maxRows, Set filterKeys, EntityMode entityMode, - ResultTransformer customTransformer) { + CacheableResultTransformer customTransformer) { this.sqlQueryString = sqlQueryString; this.positionalParameterTypes = positionalParameterTypes; this.positionalParameterValues = positionalParameterValues; @@ -176,7 +177,7 @@ public class QueryKey implements Serializable { this.hashCode = generateHashCode(); } - public ResultTransformer getResultTransformer() { + public CacheableResultTransformer getResultTransformer() { return customTransformer; } diff --git a/core/src/main/java/org/hibernate/cache/StandardQueryCache.java b/core/src/main/java/org/hibernate/cache/StandardQueryCache.java index 6f8c19c5bf..429718b353 100644 --- a/core/src/main/java/org/hibernate/cache/StandardQueryCache.java +++ b/core/src/main/java/org/hibernate/cache/StandardQueryCache.java @@ -97,6 +97,9 @@ public class StandardQueryCache implements QueryCache { } List cacheable = new ArrayList( result.size() + 1 ); + if ( log.isTraceEnabled() ) { + logCachedResultDetails( key, null, returnTypes, cacheable ); + } cacheable.add( ts ); for ( Object aResult : result ) { if ( returnTypes.length == 1 ) { @@ -107,6 +110,9 @@ public class StandardQueryCache implements QueryCache { TypeHelper.disassemble( (Object[]) aResult, returnTypes, null, session, null ) ); } + if ( log.isTraceEnabled() ) { + logCachedResultRowDetails( returnTypes, aResult ); + } } cacheRegion.put( key, cacheable ); @@ -126,6 +132,10 @@ public class StandardQueryCache implements QueryCache { } 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; @@ -157,6 +167,9 @@ public class StandardQueryCache implements QueryCache { TypeHelper.assemble( ( Serializable[] ) cacheable.get( i ), returnTypes, session, null ) ); } + if ( log.isTraceEnabled() ) { + logCachedResultRowDetails( returnTypes, result.get( i - 1 )); + } } catch ( RuntimeException ex ) { if ( isNaturalKeyLookup && @@ -202,4 +215,70 @@ public class StandardQueryCache implements QueryCache { 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 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() ); + } + } + } + } + } } diff --git a/core/src/main/java/org/hibernate/hql/classic/QueryTranslatorImpl.java b/core/src/main/java/org/hibernate/hql/classic/QueryTranslatorImpl.java index 78915efaa7..16bc2894a4 100644 --- a/core/src/main/java/org/hibernate/hql/classic/QueryTranslatorImpl.java +++ b/core/src/main/java/org/hibernate/hql/classic/QueryTranslatorImpl.java @@ -30,6 +30,7 @@ import java.sql.PreparedStatement; 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; @@ -71,7 +72,6 @@ import org.hibernate.transform.ResultTransformer; import org.hibernate.type.AssociationType; import org.hibernate.type.EntityType; import org.hibernate.type.Type; -import org.hibernate.type.TypeFactory; import org.hibernate.util.ArrayHelper; import org.hibernate.util.ReflectHelper; import org.hibernate.util.StringHelper; @@ -983,6 +983,16 @@ public class QueryTranslatorImpl extends BasicLoader implements FilterTranslator throw new UnsupportedOperationException( "Not supported! Use the AST translator..."); } + 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, @@ -992,27 +1002,28 @@ public class QueryTranslatorImpl extends BasicLoader implements FilterTranslator 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 if ( holderClass == null ) { - return row.length == 1 ? row[0] : row; } else { - return row; + resultRow = toResultRow( row ); } - + return resultRow; } protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException { diff --git a/core/src/main/java/org/hibernate/loader/Loader.java b/core/src/main/java/org/hibernate/loader/Loader.java index 70d8d0b797..437d85d6fd 100644 --- a/core/src/main/java/org/hibernate/loader/Loader.java +++ b/core/src/main/java/org/hibernate/loader/Loader.java @@ -77,6 +77,7 @@ import org.hibernate.persister.entity.Loadable; 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; @@ -255,6 +256,20 @@ public abstract class Loader { 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(); boolean defaultReadOnlyOrig = persistenceContext.isDefaultReadOnly(); if ( queryParameters.isReadOnlyInitialized() ) { @@ -271,7 +286,7 @@ public abstract class Loader { List result; try { try { - result = doQuery( session, queryParameters, returnProxies ); + result = doQuery( session, queryParameters, returnProxies, forcedResultTransformer ); } finally { persistenceContext.afterLoad(); @@ -600,7 +615,29 @@ public abstract class Loader { final List hydratedObjects, final EntityKey[] keys, boolean returnProxies) throws SQLException, HibernateException { + return getRowFromResultSet( + resultSet, + session, + queryParameters, + lockModesArray, + optionalObjectKey, + hydratedObjects, + keys, + returnProxies, + null + ); + } + private Object getRowFromResultSet( + final ResultSet resultSet, + final SessionImplementor session, + final QueryParameters queryParameters, + final LockMode[] lockModesArray, + 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; extractKeysFromResultSet( persisters, queryParameters, resultSet, session, keys, lockModesArray, hydratedObjects ); @@ -636,8 +673,13 @@ public abstract class Loader { applyPostLoadLocks( row, lockModesArray, session ); - return getResultColumnOrRow( row, queryParameters.getResultTransformer(), resultSet, session ); - + return forcedResultTransformer == null ? + getResultColumnOrRow( row, queryParameters.getResultTransformer(), resultSet, session ) : + forcedResultTransformer.transformTuple( + getResultRow( row, resultSet, session ), + getResultRowAliases() + ) + ; } protected void extractKeysFromResultSet( @@ -788,7 +830,8 @@ public abstract class Loader { 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 ) ? @@ -834,7 +877,8 @@ public abstract class Loader { optionalObjectKey, hydratedObjects, keys, - returnProxies + returnProxies, + forcedResultTransformer ); results.add( result ); @@ -1018,20 +1062,27 @@ public abstract class Loader { 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? - * @param transformer, the specified transformer * @return true, if getResultColumnOrRow() transforms the results; false, otherwise */ - protected boolean areResultSetRowsTransformedImmediately(ResultTransformer transformer) { + 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; + } + /** * Get the actual object that is returned in the user-visible result list. * This empty implementation merely returns its first argument. This is @@ -1042,6 +1093,17 @@ public abstract class Loader { return row; } + protected boolean[] includeInResultRow() { + return null; + } + + protected Object[] getResultRow(Object[] row, + ResultSet rs, + SessionImplementor session) + throws SQLException, HibernateException { + return row; + } + /** * For missing objects associated by one-to-one with another object in the * result set, register the fact that the the object is missing with the @@ -2284,21 +2346,8 @@ public abstract class Loader { QueryCache queryCache = factory.getQueryCache( queryParameters.getCacheRegion() ); - Set filterKeys = FilterKey.createFilterKeys( - session.getLoadQueryInfluencers().getEnabledFilters(), - session.getEntityMode() - ); - QueryKey key = QueryKey.generateQueryKey( - getSQLString(), - queryParameters, - filterKeys, - session, - ( areResultSetRowsTransformedImmediately( queryParameters.getResultTransformer() ) ? - queryParameters.getResultTransformer() : - null - ) - ); - + QueryKey key = generateQueryKey( session, queryParameters ); + if ( querySpaces == null || querySpaces.size() == 0 ) { log.trace( "unexpected querySpaces is "+( querySpaces == null ? "null" : "empty" ) ); } @@ -2309,14 +2358,14 @@ public abstract class Loader { List result = getResultFromQueryCache( session, queryParameters, - querySpaces, + querySpaces, resultTypes, queryCache, key ); if ( result == null ) { - result = doList( session, queryParameters ); + result = doList( session, queryParameters, key.getResultTransformer() ); putResultInQueryCache( session, @@ -2328,10 +2377,48 @@ public abstract class Loader { ); } + 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.getLoadQueryInfluencers().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, @@ -2358,33 +2445,18 @@ public abstract class Loader { queryParameters.setReadOnly( persistenceContext.isDefaultReadOnly() ); } try { - result = queryCache.get( key, resultTypes, isImmutableNaturalKeyLookup, querySpaces, session ); - logCachedResultDetails( - key.getResultTransformer(), - resultTypes, - result + result = queryCache.get( + key, + key.getResultTransformer().getCachedResultTypes( resultTypes ), + isImmutableNaturalKeyLookup, + querySpaces, + session ); } finally { persistenceContext.setDefaultReadOnly( defaultReadOnlyOrig ); } - // If there is a result transformer, but the loader is not expecting the data to be - // transformed yet, then the loader expects result elements that are Object[]. - // The problem is that StandardQueryCache.get(...) does not return a tuple when - // resultTypes.length == 1. The following changes the data returned from the cache - // to be a tuple. - // TODO: this really doesn't belong here, but only Loader has the information - // to be able to do this. - if ( result != null && - resultTypes.length == 1 && - key.getResultTransformer() == null && - resolveResultTransformer( queryParameters.getResultTransformer() ) != null ) { - for ( int i = 0 ; i < result.size() ; i++ ) { - result.set( i, new Object[] { result.get( i ) } ); - } - } - if ( factory.getStatistics().isStatisticsEnabled() ) { if ( result == null ) { factory.getStatisticsImplementor() @@ -2408,30 +2480,13 @@ public abstract class Loader { final QueryKey key, final List result) { if ( session.getCacheMode().isPutEnabled() ) { - if ( log.isTraceEnabled() ) { - logCachedResultDetails( - key.getResultTransformer(), - resultTypes, - result - ); - } - // If there is a result transformer, but the data has not been transformed yet, - // then result elements are Object[]. The problem is that StandardQueryCache.put(...) - // does not expect a tuple when resultTypes.length == 1. The following changes the - // data being cached to what StandardQueryCache.put(...) expects. - // TODO: this really doesn't belong here, but only Loader has the information - // to be able to do this. - List cachedResult = result; - if ( resultTypes.length == 1 && - key.getResultTransformer() == null && - resolveResultTransformer( queryParameters.getResultTransformer() ) != null ) { - cachedResult = new ArrayList( result.size() ); - for ( int i = 0 ; i < result.size() ; i++ ) { - cachedResult.add( ( ( Object[] ) result.get( i ) )[ 0 ] ); - } - } - - boolean put = queryCache.put( key, resultTypes, cachedResult, 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() ); @@ -2439,90 +2494,19 @@ public abstract class Loader { } } - private void logCachedResultDetails(ResultTransformer resultTransformer, Type[] returnTypes, List result) { - if ( ! log.isTraceEnabled() ) { - return; - } - if ( returnTypes == null || returnTypes.length == 0 ) { - log.trace( "unexpected returnTypes is "+( returnTypes == null ? "null" : "empty" )+ - "! transformer="+( resultTransformer == null ? "null" : resultTransformer.getClass().getName() )+ - " result"+( result == null ? " is null": ".size()=" + result.size() ) ); - } - else { - StringBuffer returnTypeNames = new StringBuffer(); - StringBuffer returnClassNames = new StringBuffer(); - for ( int i=0; i 1 ) { - log.trace( "unexpected result value! "+ - "transformer="+( resultTransformer == null ? "null" : resultTransformer.getClass().getName() )+ - "value is null; should be Object["+returnTypes.length+"]!" ); - } - } - else { - if ( returnTypes == null || returnTypes.length == 0 ) { - log.trace( "unexpected result value! "+ - "transformer="+( resultTransformer == null ? "null" : resultTransformer.getClass().getName() )+ - "value is non-null; returnTypes is "+( returnTypes == null ? "null" : "empty" ) ); - } - else if ( Object[].class.isInstance( value ) ) { - Object[] tuple = ( Object[] ) value; - log.trace( "transformer="+( resultTransformer == null ? "null" : resultTransformer.getClass().getName() )+ - " value is Object["+tuple.length+ - "]; returnTypes is Type["+returnTypes.length+"]" ); - if ( tuple.length != returnTypes.length ) { - log.trace( "unexpected tuple length! transformer="+ - ( resultTransformer == null ? "null" : resultTransformer.getClass().getName() )+ - " 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="+ - ( resultTransformer == null ? "null" : resultTransformer.getClass().getName() )+ - " expected="+returnTypes[ j ].getReturnedClass().getName()+ - " got="+tuple[ j ].getClass().getName() ); - } - } - } - } - else { - if ( returnTypes.length != 1 ) { - log.trace( "unexpected number of result columns! should be Object["+returnTypes.length+"]! transformer="+ - ( resultTransformer == null ? "null" : resultTransformer.getClass().getName() )+ - " value type="+value.getClass().getName()+ - " returnTypes is Type["+returnTypes.length+"]" ); - } - else if ( ! returnTypes[ 0 ].getReturnedClass().isInstance( value ) ) { - log.trace( "unexpected value type! transformer="+ - ( resultTransformer == null ? "null" : resultTransformer.getClass().getName() )+ - " expected="+returnTypes[ 0 ].getReturnedClass().getName()+ - " got="+ value.getClass().getName() ); - } - } - } - } - } - } - - /** * Actually execute a query, ignoring the query cache */ + protected List doList(final SessionImplementor session, final QueryParameters queryParameters) throws HibernateException { + return doList( session, queryParameters, null); + } + + private List doList(final SessionImplementor session, + final QueryParameters queryParameters, + final ResultTransformer forcedResultTransformer) + throws HibernateException { final boolean stats = getFactory().getStatistics().isStatisticsEnabled(); long startTime = 0; @@ -2530,7 +2514,7 @@ public abstract class Loader { List result; try { - result = doQueryAndInitializeNonLazyCollections( session, queryParameters, true ); + result = doQueryAndInitializeNonLazyCollections( session, queryParameters, true, forcedResultTransformer ); } catch ( SQLException sqle ) { throw JDBCExceptionHelper.convert( diff --git a/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java b/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java index 633527652e..181433be4b 100755 --- a/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java +++ b/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java @@ -25,6 +25,7 @@ package org.hibernate.loader.criteria; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Set; @@ -59,10 +60,14 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker { 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; @@ -72,6 +77,10 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker { return userAliases; } + public boolean[] includeInResultRow() { + return includeInResultRow; + } + public CriteriaJoinWalker( final OuterJoinLoadable persister, final CriteriaQueryTranslator translator, @@ -96,26 +105,29 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker { querySpaces = translator.getQuerySpaces(); - if ( translator.hasProjection() ) { - resultTypes = translator.getProjectedTypes(); - - initProjection( + if ( translator.hasProjection() ) { + initProjection( translator.getSelect(), translator.getWhereCondition(), translator.getOrderBy(), translator.getGroupBy(), LockOptions.NONE ); + resultTypes = translator.getProjectedTypes(); + userAliases = translator.getProjectedAliases(); + includeInResultRow = new boolean[ resultTypes.length ]; + Arrays.fill( includeInResultRow, true ); } else { - resultTypes = new Type[] { factory.getTypeResolver().getTypeFactory().manyToOne( persister.getEntityName() ) }; - initAll( translator.getWhereCondition(), translator.getOrderBy(), LockOptions.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( @@ -208,15 +220,24 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker { } protected String generateTableAlias(int n, PropertyPath path, Joinable joinable) { + // TODO: deal with side-effects (changes to includeInSelectList, userAliasList, resultTypeList)!!! if ( joinable.consumesEntityAlias() ) { final Criteria subcriteria = translator.getCriteria( path.getFullPath() ); String sqlAlias = subcriteria==null ? null : translator.getSQLAlias(subcriteria); if (sqlAlias!=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() ); //alias may be null + 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 ); diff --git a/core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java b/core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java index 7049bdcee9..f34222372d 100644 --- a/core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java +++ b/core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java @@ -71,6 +71,8 @@ public class CriteriaLoader extends OuterJoinLoader { //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, @@ -102,6 +104,8 @@ public class CriteriaLoader extends OuterJoinLoader { userAliases = walker.getUserAliases(); resultTypes = walker.getResultTypes(); + includeInResultRow = walker.includeInResultRow(); + resultRowLength = ArrayHelper.countTrue( includeInResultRow ); postInstantiate(); @@ -120,20 +124,33 @@ public class CriteriaLoader extends OuterJoinLoader { } + protected String[] getResultRowAliases() { + return userAliases; + } + protected ResultTransformer resolveResultTransformer(ResultTransformer resultTransformer) { return translator.getRootCriteria().getResultTransformer(); } - protected boolean areResultSetRowsTransformedImmediately( ResultTransformer transformer ) { - // comparing to null just in case there is no transformer - // (there should always be a result transformer; - return resolveResultTransformer( transformer ) != null; + protected boolean areResultSetRowsTransformedImmediately() { + return true; + } + + protected boolean[] includeInResultRow() { + return includeInResultRow; } 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]; @@ -149,13 +166,25 @@ public class CriteriaLoader extends OuterJoinLoader { } pos += numColumns; } - aliases = translator.getProjectedAliases(); } else { - result = row; - aliases = userAliases; + result = toResultRow( row ); + } + 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; } - return resolveResultTransformer( transformer ).transformTuple(result, aliases); } public Set getQuerySpaces() { diff --git a/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java b/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java index bdcaad0d9a..f235f75784 100755 --- a/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java +++ b/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java @@ -55,6 +55,8 @@ import org.hibernate.impl.CriteriaImpl; import org.hibernate.persister.entity.Loadable; import org.hibernate.persister.entity.PropertyMapping; import org.hibernate.persister.entity.Queryable; +import org.hibernate.transform.CacheableResultTransformer; +import org.hibernate.transform.TupleSubsetResultTransformer; import org.hibernate.type.AssociationType; import org.hibernate.type.StringRepresentableType; import org.hibernate.type.Type; @@ -358,6 +360,11 @@ public class CriteriaQueryTranslator implements CriteriaQuery { ); } + /* package-protected */ + Type getResultType(Criteria criteria) { + return getFactory().getTypeResolver().getTypeFactory().manyToOne( getEntityName( criteria ) ); + } + public Type[] getProjectedTypes() { return rootCriteria.getProjection().getTypes( rootCriteria, this ); } diff --git a/core/src/main/java/org/hibernate/loader/custom/CustomLoader.java b/core/src/main/java/org/hibernate/loader/custom/CustomLoader.java index c0d0cfc751..7064025fea 100755 --- a/core/src/main/java/org/hibernate/loader/custom/CustomLoader.java +++ b/core/src/main/java/org/hibernate/loader/custom/CustomLoader.java @@ -52,7 +52,6 @@ import org.hibernate.persister.entity.Loadable; import org.hibernate.persister.entity.Queryable; import org.hibernate.transform.ResultTransformer; import org.hibernate.type.Type; -import org.hibernate.type.TypeFactory; import org.hibernate.type.EntityType; import org.hibernate.type.CollectionType; import org.hibernate.util.ArrayHelper; @@ -83,6 +82,8 @@ public class CustomLoader extends Loader { private final LockMode[] lockModes; + private boolean[] includeInResultRow; + // private final String[] sqlAliases; // private final String[] sqlAliasSuffixes; private final ResultRowProcessor rowProcessor; @@ -94,7 +95,6 @@ public class CustomLoader extends Loader { // this is only needed (afaict) for ResultTransformer processing... private String[] transformerAliases; - public CustomLoader(CustomQuery customQuery, SessionFactoryImplementor factory) { super( factory ); @@ -118,6 +118,8 @@ public class CustomLoader extends Loader { int returnableCounter = 0; boolean hasScalars = false; + List includeInResultRowList = new ArrayList(); + Iterator itr = customQuery.getCustomQueryReturns().iterator(); while ( itr.hasNext() ) { final Return rtn = ( Return ) itr.next(); @@ -131,6 +133,7 @@ public class CustomLoader extends Loader { scalarRtn.getType() ) ); + includeInResultRowList.add( true ); hasScalars = true; } else if ( rtn instanceof RootReturn ) { @@ -145,6 +148,7 @@ public class CustomLoader extends Loader { 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; @@ -167,6 +171,7 @@ public class CustomLoader extends Loader { entityAliases.add( collRtn.getElementEntityAliases() ); ArrayHelper.addAll( querySpaces, elementPersister.getQuerySpaces() ); } + includeInResultRowList.add( true ); } else if ( rtn instanceof EntityFetchReturn ) { EntityFetchReturn fetchRtn = ( EntityFetchReturn ) rtn; @@ -183,6 +188,7 @@ public class CustomLoader extends Loader { 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; @@ -206,6 +212,7 @@ public class CustomLoader extends Loader { entityAliases.add( fetchRtn.getElementEntityAliases() ); ArrayHelper.addAll( querySpaces, elementPersister.getQuerySpaces() ); } + includeInResultRowList.add( false ); } else { throw new HibernateException( "unexpected custom query return type : " + rtn.getClass().getName() ); @@ -244,6 +251,8 @@ public class CustomLoader extends Loader { hasScalars, ( ResultColumnProcessor[] ) resultColumnProcessors.toArray( new ResultColumnProcessor[ resultColumnProcessors.size() ] ) ); + + this.includeInResultRow = ArrayHelper.toBooleanArray( includeInResultRowList ); } private Queryable determineAppropriateOwnerPersister(NonScalarReturn ownerDescriptor) { @@ -336,10 +345,18 @@ public class CustomLoader extends Loader { } } + 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, @@ -348,6 +365,11 @@ public class CustomLoader extends Loader { 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( @@ -438,6 +460,17 @@ public class CustomLoader extends Loader { 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; @@ -452,11 +485,7 @@ public class CustomLoader extends Loader { } } - return ( hasTransformer ) - ? resultRow - : ( resultRow.length == 1 ) - ? resultRow[0] - : resultRow; + return resultRow; } } diff --git a/core/src/main/java/org/hibernate/loader/hql/QueryLoader.java b/core/src/main/java/org/hibernate/loader/hql/QueryLoader.java index 2916a62664..b5c4b183a3 100644 --- a/core/src/main/java/org/hibernate/loader/hql/QueryLoader.java +++ b/core/src/main/java/org/hibernate/loader/hql/QueryLoader.java @@ -27,8 +27,8 @@ package org.hibernate.loader.hql; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -39,7 +39,6 @@ import org.hibernate.QueryException; import org.hibernate.ScrollableResults; import org.hibernate.LockOptions; import org.hibernate.dialect.Dialect; -import org.hibernate.engine.EntityEntry; import org.hibernate.engine.QueryParameters; import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.engine.SessionImplementor; @@ -382,36 +381,49 @@ public class QueryLoader extends BasicLoader { return implicitResultTransformer != null; } + protected String[] getResultRowAliases() { + return queryReturnAliases; + } + protected ResultTransformer resolveResultTransformer(ResultTransformer resultTransformer) { return HolderInstantiator.resolveResultTransformer( implicitResultTransformer, 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) throws SQLException, HibernateException { - row = toResultRow( row ); + 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 if ( !hasTransform ) { - return row.length == 1 ? row[0] : row; } else { - return row; + resultRow = toResultRow( row ); } - + return resultRow; } protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException { diff --git a/core/src/main/java/org/hibernate/transform/AliasToBeanResultTransformer.java b/core/src/main/java/org/hibernate/transform/AliasToBeanResultTransformer.java index 58d93cbaf4..c560ec1511 100644 --- a/core/src/main/java/org/hibernate/transform/AliasToBeanResultTransformer.java +++ b/core/src/main/java/org/hibernate/transform/AliasToBeanResultTransformer.java @@ -24,9 +24,7 @@ */ package org.hibernate.transform; -import java.io.Serializable; import java.util.Arrays; -import java.util.List; import org.hibernate.HibernateException; import org.hibernate.property.ChainedPropertyAccessor; @@ -54,7 +52,7 @@ import org.hibernate.property.Setter; * * @author max */ -public class AliasToBeanResultTransformer implements ResultTransformer, Serializable { +public class AliasToBeanResultTransformer extends AliasedTupleSubsetResultTransformer { // IMPL NOTE : due to the delayed population of setters (setters cached // for performance), we really cannot properly define equality for @@ -73,6 +71,13 @@ public class AliasToBeanResultTransformer implements ResultTransformer, Serializ this.resultClass = resultClass; } + /** + * {@inheritDoc} + */ + public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) { + return false; + } + public Object transformTuple(Object[] tuple, String[] aliases) { Object result; @@ -129,10 +134,6 @@ public class AliasToBeanResultTransformer implements ResultTransformer, Serializ } } - public List transformList(List collection) { - return collection; - } - public boolean equals(Object o) { if ( this == o ) { return true; diff --git a/core/src/main/java/org/hibernate/transform/AliasToEntityMapResultTransformer.java b/core/src/main/java/org/hibernate/transform/AliasToEntityMapResultTransformer.java index 3c4babff00..0b00b76b3c 100644 --- a/core/src/main/java/org/hibernate/transform/AliasToEntityMapResultTransformer.java +++ b/core/src/main/java/org/hibernate/transform/AliasToEntityMapResultTransformer.java @@ -26,7 +26,6 @@ package org.hibernate.transform; import java.util.HashMap; import java.util.Map; -import java.io.Serializable; /** * {@link ResultTransformer} implementation which builds a map for each "row", @@ -38,7 +37,7 @@ import java.io.Serializable; * @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(); @@ -62,6 +61,13 @@ public class AliasToEntityMapResultTransformer extends BasicTransformerAdapter i return result; } + /** + * {@inheritDoc} + */ + public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) { + return false; + } + /** * Serialization hook for ensuring singleton uniqueing. * diff --git a/core/src/main/java/org/hibernate/transform/AliasedTupleSubsetResultTransformer.java b/core/src/main/java/org/hibernate/transform/AliasedTupleSubsetResultTransformer.java new file mode 100644 index 0000000000..8c1badf196 --- /dev/null +++ b/core/src/main/java/org/hibernate/transform/AliasedTupleSubsetResultTransformer.java @@ -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 diff --git a/core/src/main/java/org/hibernate/transform/CacheableResultTransformer.java b/core/src/main/java/org/hibernate/transform/CacheableResultTransformer.java new file mode 100644 index 0000000000..046143182d --- /dev/null +++ b/core/src/main/java/org/hibernate/transform/CacheableResultTransformer.java @@ -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. + *

+ * 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. + *

+ * If not unnecessary, the original List is returned + * unchanged. + *

+ * 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[] index(Class 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[] unindex(Class 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; + } +} diff --git a/core/src/main/java/org/hibernate/transform/DistinctResultTransformer.java b/core/src/main/java/org/hibernate/transform/DistinctResultTransformer.java index f56a490159..e255dedb09 100644 --- a/core/src/main/java/org/hibernate/transform/DistinctResultTransformer.java +++ b/core/src/main/java/org/hibernate/transform/DistinctResultTransformer.java @@ -42,7 +42,7 @@ import org.slf4j.LoggerFactory; * * @author Steve Ebersole */ -public class DistinctResultTransformer extends BasicTransformerAdapter implements Serializable { +public class DistinctResultTransformer extends BasicTransformerAdapter { public static final DistinctResultTransformer INSTANCE = new DistinctResultTransformer(); diff --git a/core/src/main/java/org/hibernate/transform/DistinctRootEntityResultTransformer.java b/core/src/main/java/org/hibernate/transform/DistinctRootEntityResultTransformer.java index 656b39b0ff..1295f737bd 100644 --- a/core/src/main/java/org/hibernate/transform/DistinctRootEntityResultTransformer.java +++ b/core/src/main/java/org/hibernate/transform/DistinctRootEntityResultTransformer.java @@ -25,7 +25,6 @@ package org.hibernate.transform; import java.util.List; -import java.io.Serializable; /** * Much like {@link RootEntityResultTransformer}, but we also distinct @@ -37,7 +36,7 @@ import java.io.Serializable; * @author Gavin King * @author Steve Ebersole */ -public class DistinctRootEntityResultTransformer implements ResultTransformer, Serializable { +public class DistinctRootEntityResultTransformer implements TupleSubsetResultTransformer { public static final DistinctRootEntityResultTransformer INSTANCE = new DistinctRootEntityResultTransformer(); @@ -68,6 +67,20 @@ public class DistinctRootEntityResultTransformer implements ResultTransformer, S 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. * diff --git a/core/src/main/java/org/hibernate/transform/PassThroughResultTransformer.java b/core/src/main/java/org/hibernate/transform/PassThroughResultTransformer.java index 44cb9c41ee..299641b498 100644 --- a/core/src/main/java/org/hibernate/transform/PassThroughResultTransformer.java +++ b/core/src/main/java/org/hibernate/transform/PassThroughResultTransformer.java @@ -24,14 +24,17 @@ */ package org.hibernate.transform; -import java.io.Serializable; +import java.util.Arrays; +import java.util.List; + +import org.hibernate.util.ArrayHelper; /** * ??? * * @author max */ -public class PassThroughResultTransformer extends BasicTransformerAdapter implements Serializable { +public class PassThroughResultTransformer extends BasicTransformerAdapter implements TupleSubsetResultTransformer { public static final PassThroughResultTransformer INSTANCE = new PassThroughResultTransformer(); @@ -48,6 +51,39 @@ public class PassThroughResultTransformer extends BasicTransformerAdapter implem return tuple.length==1 ? tuple[0] : tuple; } + /** + * {@inheritDoc} + */ + public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) { + return tupleLength == 1; + } + + /** + * {@inheritDoc} + */ + public boolean[] includeInTransform(String[] aliases, int tupleLength) { + boolean[] includeInTransformedResult = new boolean[tupleLength]; + Arrays.fill( includeInTransformedResult, true ); + return includeInTransformedResult; + } + + /* 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. * diff --git a/core/src/main/java/org/hibernate/transform/RootEntityResultTransformer.java b/core/src/main/java/org/hibernate/transform/RootEntityResultTransformer.java index 66a982951f..35b3b97889 100644 --- a/core/src/main/java/org/hibernate/transform/RootEntityResultTransformer.java +++ b/core/src/main/java/org/hibernate/transform/RootEntityResultTransformer.java @@ -24,7 +24,7 @@ */ package org.hibernate.transform; -import java.io.Serializable; +import org.hibernate.util.ArrayHelper; /** * {@link ResultTransformer} implementation which limits the result tuple @@ -36,7 +36,7 @@ import java.io.Serializable; * @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(); @@ -53,6 +53,29 @@ public final class RootEntityResultTransformer extends BasicTransformerAdapter i 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. * diff --git a/core/src/main/java/org/hibernate/transform/ToListResultTransformer.java b/core/src/main/java/org/hibernate/transform/ToListResultTransformer.java index f28e411316..b32a0df1a3 100644 --- a/core/src/main/java/org/hibernate/transform/ToListResultTransformer.java +++ b/core/src/main/java/org/hibernate/transform/ToListResultTransformer.java @@ -26,13 +26,12 @@ package org.hibernate.transform; import java.util.Arrays; import java.util.List; -import java.io.Serializable; /** * 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(); diff --git a/core/src/main/java/org/hibernate/transform/TupleSubsetResultTransformer.java b/core/src/main/java/org/hibernate/transform/TupleSubsetResultTransformer.java new file mode 100644 index 0000000000..a7b48de839 --- /dev/null +++ b/core/src/main/java/org/hibernate/transform/TupleSubsetResultTransformer.java @@ -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: + *

    + *
  1. + * 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; + *
  2. + *
  3. + * any tuple elements included in the transformed value are + * unmodified by the transformation; + *
  4. + *
  5. + * transforming equivalent tuples with the same aliases multiple + * times results in transformed values that are equivalent; + *
  6. + *
  7. + * 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; + *
  8. + *
  9. + * 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; + *
  10. + *
+ * + * @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 diff --git a/core/src/test/java/org/hibernate/cache/QueryKeyTest.java b/core/src/test/java/org/hibernate/cache/QueryKeyTest.java index bad6a570bc..6925231db3 100644 --- a/core/src/test/java/org/hibernate/cache/QueryKeyTest.java +++ b/core/src/test/java/org/hibernate/cache/QueryKeyTest.java @@ -27,17 +27,25 @@ 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.HibernateException; +import org.hibernate.transform.AliasToBeanConstructorResultTransformer; import org.hibernate.transform.AliasToBeanResultTransformer; -import org.hibernate.transform.RootEntityResultTransformer; +import org.hibernate.transform.AliasedTupleSubsetResultTransformer; +import org.hibernate.transform.CacheableResultTransformer; +import org.hibernate.transform.DistinctResultTransformer; import org.hibernate.transform.ResultTransformer; +import org.hibernate.transform.RootEntityResultTransformer; import org.hibernate.transform.DistinctRootEntityResultTransformer; import org.hibernate.transform.AliasToEntityMapResultTransformer; import org.hibernate.transform.PassThroughResultTransformer; -import org.hibernate.transform.DistinctResultTransformer; +import org.hibernate.transform.ToListResultTransformer; +import org.hibernate.transform.TupleSubsetResultTransformer; +import org.hibernate.type.SerializationException; import org.hibernate.util.SerializationHelper; import org.hibernate.util.ArrayHelper; @@ -52,6 +60,14 @@ public class QueryKeyTest extends TestCase { 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; @@ -61,31 +77,156 @@ public class QueryKeyTest extends TestCase { this.propAccessedByMethod = propAccessedByMethod; } } - public void testSerializedEquality() { - doTest( buildBasicKey( null ) ); - } - - 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 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) - doTest( buildBasicKey( new AliasToBeanResultTransformer( AClass.class ) ) ); + 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" } ); - doTest( buildBasicKey( transformer ) ); + 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 ); } - private QueryKey buildBasicKey(ResultTransformer resultTransformer) { + // 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 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 + } + + 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 @@ -100,18 +241,34 @@ public class QueryKeyTest extends TestCase { } 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"; + } } } diff --git a/testsuite/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java b/testsuite/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java index d52a8338b0..e88985e36e 100644 --- a/testsuite/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java +++ b/testsuite/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java @@ -2307,6 +2307,96 @@ public class ASTParserLoadingTest extends FunctionalTestCase { destroyTestBaseData(); } + public void testCachedJoinedAndJoinFetchedManyToOne() throws Exception { + + Animal a = new Animal(); + a.setDescription( "an animal" ); + Animal mother = new Animal(); + mother.setDescription( "a mother" ); + mother.addOffspring( a ); + a.setMother( mother ); + Animal offspring1 = new Animal(); + offspring1.setDescription( "offspring1" ); + Animal offspring2 = new Animal(); + offspring1.setDescription( "offspring2" ); + a.addOffspring( offspring1 ); + offspring1.setMother( a ); + a.addOffspring( offspring2 ); + offspring2.setMother( a ); + + Session s = openSession(); + Transaction t = s.beginTransaction(); + s.save( mother ); + s.save( a ); + s.save( offspring1 ); + s.save( offspring2 ); + t.commit(); + s.close(); + + getSessions().getCache().evictQueryRegions(); + getSessions().getStatistics().clear(); + + s = openSession(); + t = s.beginTransaction(); + List list = s.createQuery( "from Animal a left join fetch a.mother" ).setCacheable( true ).list(); + assertEquals( 0, getSessions().getStatistics().getQueryCacheHitCount() ); + assertEquals( 1, getSessions().getStatistics().getQueryCachePutCount() ); + list = s.createQuery( "select a from Animal a left join fetch a.mother" ).setCacheable( true ).list(); + assertEquals( 1, getSessions().getStatistics().getQueryCacheHitCount() ); + assertEquals( 1, getSessions().getStatistics().getQueryCachePutCount() ); + list = s.createQuery( "select a, m from Animal a left join a.mother m" ).setCacheable( true ).list(); + assertEquals( 1, getSessions().getStatistics().getQueryCacheHitCount() ); + assertEquals( 2, getSessions().getStatistics().getQueryCachePutCount() ); + s.createQuery( "delete from Animal" ).executeUpdate(); + t.commit(); + s.close(); + } + + public void testCachedJoinedAndJoinFetchedOneToMany() throws Exception { + + Animal a = new Animal(); + a.setDescription( "an animal" ); + Animal mother = new Animal(); + mother.setDescription( "a mother" ); + mother.addOffspring( a ); + a.setMother( mother ); + Animal offspring1 = new Animal(); + offspring1.setDescription( "offspring1" ); + Animal offspring2 = new Animal(); + offspring1.setDescription( "offspring2" ); + a.addOffspring( offspring1 ); + offspring1.setMother( a ); + a.addOffspring( offspring2 ); + offspring2.setMother( a ); + + getSessions().getCache().evictQueryRegions(); + getSessions().getStatistics().clear(); + + Session s = openSession(); + Transaction t = s.beginTransaction(); + s.save( mother ); + s.save( a ); + s.save( offspring1 ); + s.save( offspring2 ); + t.commit(); + s.close(); + + s = openSession(); + t = s.beginTransaction(); + List list = s.createQuery( "from Animal a left join fetch a.offspring" ).setCacheable( true ).list(); + assertEquals( 0, getSessions().getStatistics().getQueryCacheHitCount() ); + assertEquals( 1, getSessions().getStatistics().getQueryCachePutCount() ); + list = s.createQuery( "select a from Animal a left join fetch a.offspring" ).setCacheable( true ).list(); + assertEquals( 1, getSessions().getStatistics().getQueryCacheHitCount() ); + assertEquals( 1, getSessions().getStatistics().getQueryCachePutCount() ); + list = s.createQuery( "select a, o from Animal a left join a.offspring o" ).setCacheable( true ).list(); + assertEquals( 1, getSessions().getStatistics().getQueryCacheHitCount() ); + assertEquals( 2, getSessions().getStatistics().getQueryCachePutCount() ); + s.createQuery( "delete from Animal" ).executeUpdate(); + t.commit(); + s.close(); + } + public void testIllegalMixedTransformerQueries() { Session session = openSession(); diff --git a/testsuite/src/test/java/org/hibernate/test/querycache/AbstractQueryCacheResultTransformerTest.java b/testsuite/src/test/java/org/hibernate/test/querycache/AbstractQueryCacheResultTransformerTest.java index b08cec3749..3c65a4f262 100644 --- a/testsuite/src/test/java/org/hibernate/test/querycache/AbstractQueryCacheResultTransformerTest.java +++ b/testsuite/src/test/java/org/hibernate/test/querycache/AbstractQueryCacheResultTransformerTest.java @@ -44,6 +44,7 @@ import org.hibernate.criterion.Projections; import org.hibernate.criterion.Property; import org.hibernate.criterion.Restrictions; import org.hibernate.impl.SessionFactoryImpl; +import org.hibernate.test.fetchprofiles.join.Enrollment; import org.hibernate.testing.junit.functional.FunctionalTestCase; import org.hibernate.proxy.HibernateProxy; import org.hibernate.transform.AliasToBeanConstructorResultTransformer; @@ -101,9 +102,18 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional public Object execute(boolean isSingleResult) throws Exception{ Session s = openSession(); Transaction t = s.beginTransaction(); - Object result = getResults( s, isSingleResult ); - t.commit(); - s.close(); + 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; @@ -203,7 +213,130 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional 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) { @@ -273,6 +406,7 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional 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() ); } @@ -300,6 +434,10 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional 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() ) ); @@ -309,7 +447,7 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional runTest( hqlExecutor, criteriaExecutor, checker, false ); } - public void testEntityWithJoinFetchedLazyOneToManySingleElementList() throws Exception { + public void testEntityWithUnaliasedJoinFetchedLazyOneToManySingleElementList() throws Exception { // unaliased CriteriaExecutor criteriaExecutorUnaliased = new CriteriaExecutor() { protected Criteria getCriteria(Session s) { @@ -325,54 +463,14 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional } }; - // aliased - CriteriaExecutor criteriaExecutorAliased1 = 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 ) - .setFetchMode( "enrolments", 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.enrolments", "e", Criteria.LEFT_JOIN ) - .setFetchMode( "e", 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.enrolments", "e", Criteria.LEFT_JOIN ) - .setFetchMode( "enrolments", 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.enrolments", "e", Criteria.LEFT_JOIN ) - .setFetchMode( "e", 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.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( 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() ); @@ -383,11 +481,236 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional }; 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 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 { @@ -456,6 +779,9 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional 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() ); @@ -547,6 +873,53 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional 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) { @@ -578,6 +951,8 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional 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() ); @@ -623,6 +998,9 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional 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() ); @@ -768,89 +1146,6 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional runTest( hqlExecutorAliased, null, checker, false ); } - 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 testAliasToEntityMapOneProjectionList() throws Exception { CriteriaExecutor criteriaExecutor = new CriteriaExecutor() { protected Criteria getCriteria(Session s) { @@ -873,23 +1168,14 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional Map yogiMap = ( Map ) resultList.get( 0 ); Map shermanMap = ( Map ) resultList.get( 1 ); assertEquals( 1, yogiMap.size() ); - //assertTrue( yogiMap[ 0 ] instanceof HibernateProxy ); - assertTrue( yogiMap.get( "student" ) instanceof Student ); - if( Hibernate.isInitialized( yogiMap.get( "student" ) ) ) { - assertEquals( yogiExpected, yogiMap.get( "student" ) ); - } - else { - assertEquals( yogiExpected.getStudentNumber(), ( ( Student ) yogiMap.get( "student" ) ).getStudentNumber() ); - } assertEquals( 1, shermanMap.size() ); - //assertTrue( shermanMap[ 0 ] instanceof HibernateProxy ); + // 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 ); - if( Hibernate.isInitialized( shermanMap.get( "student" ) ) ) { - assertEquals( shermanExpected, shermanMap.get( "student" ) ); - } - else { - assertEquals( shermanExpected.getStudentNumber(), ( ( Student ) shermanMap.get( "student" ) ).getStudentNumber() ); - } + assertEquals( yogiExpected.getStudentNumber(), ( ( Student ) yogiMap.get( "student" ) ).getStudentNumber() ); + assertEquals( shermanExpected.getStudentNumber(), ( ( Student ) shermanMap.get( "student" ) ).getStudentNumber() ); } }; runTest( hqlExecutor, criteriaExecutor, checker, false); @@ -923,26 +1209,17 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional Map yogiMap = ( Map ) resultList.get( 0 ); Map shermanMap = ( Map ) resultList.get( 1 ); assertEquals( 4, yogiMap.size() ); - //assertTrue( yogiMap[ 0 ] instanceof HibernateProxy ); + assertEquals( 4, shermanMap.size() ); assertTrue( yogiMap.get( "student" ) instanceof Student ); - if( Hibernate.isInitialized( yogiMap.get( "student" ) ) ) { - assertEquals( yogiExpected, yogiMap.get( "student" ) ); - } - else { - assertEquals( yogiExpected.getStudentNumber(), ( ( Student ) yogiMap.get( "student" ) ).getStudentNumber() ); - } + 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( 4, shermanMap.size() ); - //assertTrue( shermanMap[ 0 ] instanceof HibernateProxy ); - assertTrue( shermanMap.get( "student" ) instanceof Student ); - if( Hibernate.isInitialized( shermanMap.get( "student" ) ) ) { - assertEquals( shermanExpected, shermanMap.get( "student" ) ); - } - else { - assertEquals( shermanExpected.getStudentNumber(), ( ( Student ) shermanMap.get( "student" ) ).getStudentNumber() ); - } assertEquals( shermanEnrolmentExpected.getSemester(), shermanMap.get( "semester" ) ); assertEquals( shermanEnrolmentExpected.getYear(), shermanMap.get( "year" ) ); assertEquals( courseExpected, shermanMap.get( "course" ) ); @@ -978,27 +1255,15 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional assertEquals( 2, resultList.size() ); Map yogiMap = ( Map ) resultList.get( 0 ); Map shermanMap = ( Map ) resultList.get( 1 ); - //assertEquals( 2, yogiMap.size() ); - //assertTrue( yogiMap[ 0 ] instanceof HibernateProxy ); + // 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 ); - if( Hibernate.isInitialized( yogiMap.get( "student" ) ) ) { - assertEquals( yogiExpected, yogiMap.get( "student" ) ); - } - else { - assertEquals( yogiExpected.getStudentNumber(), ( ( Student ) yogiMap.get( "student" ) ).getStudentNumber() ); - } + 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" ) ); - //assertEquals( 2, shermanMap.size() ); - //assertTrue( shermanMap[ 0 ] instanceof HibernateProxy ); - assertTrue( shermanMap.get( "student" ) instanceof Student ); - if( Hibernate.isInitialized( shermanMap.get( "student" ) ) ) { - assertEquals( shermanExpected, shermanMap.get( "student" ) ); - } - else { - assertEquals( shermanExpected.getStudentNumber(), ( ( Student ) shermanMap.get( "student" ) ).getStudentNumber() ); - } assertNull( shermanMap.get( "semester" ) ); assertNull( shermanMap.get( "year" ) ); assertEquals( courseExpected, shermanMap.get( "course" ) ); @@ -1135,12 +1400,9 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional public void check(Object results) { assertTrue( results instanceof Student ); Student student = ( Student ) results; - if ( Hibernate.isInitialized( student ) ) { - assertEquals( yogiExpected, student ); - } - else { - assertEquals( yogiExpected.getStudentNumber(), student.getStudentNumber() ); - } + // 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 ); @@ -1164,18 +1426,11 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional public void check(Object results) { List resultList = ( List ) results; assertEquals( 2, resultList.size() ); - if ( Hibernate.isInitialized( resultList.get( 0 ) ) ) { - assertEquals( yogiExpected, resultList.get( 0 ) ); - } - else { - assertEquals( yogiExpected.getStudentNumber(), ( ( Student ) resultList.get( 0 ) ).getStudentNumber() ); - } - if ( Hibernate.isInitialized( resultList.get( 1 ) ) ) { - assertEquals( shermanExpected, resultList.get( 1 ) ); - } - else { - assertEquals( shermanExpected.getStudentNumber(), ( ( Student ) resultList.get( 1 ) ).getStudentNumber() ); - } + // 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 ); @@ -1208,11 +1463,10 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional assertTrue( results instanceof Object[] ); Object shermanObjects[] = ( Object [] ) results; assertEquals( 4, shermanObjects.length ); - //assertTrue( shermanObjects[ 0 ] instanceof HibernateProxy ); + assertNotNull( shermanObjects[ 0 ] ); assertTrue( shermanObjects[ 0 ] instanceof Student ); - if ( Hibernate.isInitialized( shermanObjects[ 0 ] ) ) { - assertEquals( shermanExpected, shermanObjects[ 0 ] ); - } + // 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 ) ); @@ -1250,22 +1504,60 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional Object[] yogiObjects = ( Object[] ) resultList.get( 0 ); Object[] shermanObjects = ( Object[] ) resultList.get( 1 ); assertEquals( 4, yogiObjects.length ); - //assertTrue( yogiObjects[ 0 ] instanceof HibernateProxy ); + // 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 ); - if( Hibernate.isInitialized( yogiObjects[ 0 ] ) ) { - assertEquals( yogiExpected, yogiObjects[ 0 ] ); - } + 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 ] ); - //assertTrue( shermanObjects[ 0 ] instanceof HibernateProxy ); - assertTrue( shermanObjects[ 0 ] instanceof Student ); - if ( Hibernate.isInitialized( shermanObjects[ 0 ] ) ) { - assertEquals( shermanExpected, 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 ] ); } @@ -1516,7 +1808,7 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional runTest( hqlExecutor, criteriaExecutor, checker, false ); } - public void testOneSelectNewList() throws Exception { + public void testOneSelectNewNoAliasesList() throws Exception { CriteriaExecutor criteriaExecutor = new CriteriaExecutor() { protected Criteria getCriteria(Session s) throws Exception { return s.createCriteria( Student.class, "s" ) @@ -1548,14 +1840,46 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional 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( PersonName.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" ) ) - .add( Property.forName( "s.name" ) ) + .add( Property.forName( "s.studentNumber" ).as( "studentNumber" )) + .add( Property.forName( "s.name" ).as( "name" )) ) .addOrder( Order.asc( "s.studentNumber" ) ) .setResultTransformer( new AliasToBeanConstructorResultTransformer( getConstructor() ) ); @@ -1590,8 +1914,8 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional return s.createCriteria( Student.class, "s" ) .setProjection( Projections.projectionList() - .add( Projections.sqlProjection( "555 as sCode", new String[]{ "sCode" }, new Type[] { Hibernate.LONG } ) ) - .add( Property.forName( "s.name" ) ) + .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() ) ); @@ -1627,8 +1951,8 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional return s.createCriteria( Student.class, "s" ) .setProjection( Projections.projectionList() - .add( Property.forName( "s.studentNumber" ) ) - .add( Property.forName( "s.name" ) ) + .add( Property.forName( "s.studentNumber" ).as( "studentNumber" )) + .add( Property.forName( "s.name" ).as( "name" ) ) ) .addOrder( Order.asc( "s.studentNumber" ) ) .setResultTransformer( Transformers.TO_LIST ); @@ -1654,7 +1978,142 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional runTest( hqlExecutor, criteriaExecutor, checker, false ); } - public void testMultiSelectNewMapList() throws Exception { + 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" ) @@ -1693,8 +2152,8 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional return s.createCriteria( Student.class, "s" ) .setProjection( Projections.projectionList() - .add( Property.forName( "s.studentNumber" ) ) - .add( Property.forName( "s.name" ) ) + .add( Property.forName( "s.studentNumber" ).as( "studentNumber" ) ) + .add( Property.forName( "s.name" ).as( "name" ) ) ) .addOrder( Order.asc( "s.studentNumber" ) ) .setResultTransformer( new AliasToBeanConstructorResultTransformer( getConstructor() ) ); @@ -1848,13 +2307,17 @@ public abstract class AbstractQueryCacheResultTransformerTest extends Functional protected void runTest(HqlExecutor hqlExecutor, CriteriaExecutor criteriaExecutor, ResultChecker checker, boolean isSingleResult) throws Exception { createData(); - if ( criteriaExecutor != null ) { - runTest( criteriaExecutor, checker, isSingleResult ); + try { + if ( criteriaExecutor != null ) { + runTest( criteriaExecutor, checker, isSingleResult ); + } + if ( hqlExecutor != null ) { + runTest( hqlExecutor, checker, isSingleResult ); + } } - if ( hqlExecutor != null ) { - runTest( hqlExecutor, checker, isSingleResult ); + finally { + deleteData(); } - deleteData(); } private boolean isQueryCacheGetEnabled() { diff --git a/testsuite/src/test/java/org/hibernate/test/querycache/CriteriaQueryCacheIgnoreResultTransformerTest.java b/testsuite/src/test/java/org/hibernate/test/querycache/CriteriaQueryCacheIgnoreResultTransformerTest.java index 3ae4cf55a9..691193bf64 100644 --- a/testsuite/src/test/java/org/hibernate/test/querycache/CriteriaQueryCacheIgnoreResultTransformerTest.java +++ b/testsuite/src/test/java/org/hibernate/test/querycache/CriteriaQueryCacheIgnoreResultTransformerTest.java @@ -49,9 +49,13 @@ public class CriteriaQueryCacheIgnoreResultTransformerTest extends AbstractQuery protected void runTest(HqlExecutor hqlExecutor, CriteriaExecutor criteriaExecutor, ResultChecker checker, boolean isSingleResult) throws Exception { createData(); - if ( criteriaExecutor != null ) { - runTest( criteriaExecutor, checker, isSingleResult ); + try { + if ( criteriaExecutor != null ) { + runTest( criteriaExecutor, checker, isSingleResult ); + } + } + finally { + deleteData(); } - deleteData(); } } diff --git a/testsuite/src/test/java/org/hibernate/test/querycache/CriteriaQueryCachePutResultTransformerTest.java b/testsuite/src/test/java/org/hibernate/test/querycache/CriteriaQueryCachePutResultTransformerTest.java index 2e928e5864..cdb7da2348 100644 --- a/testsuite/src/test/java/org/hibernate/test/querycache/CriteriaQueryCachePutResultTransformerTest.java +++ b/testsuite/src/test/java/org/hibernate/test/querycache/CriteriaQueryCachePutResultTransformerTest.java @@ -48,134 +48,5 @@ public class CriteriaQueryCachePutResultTransformerTest extends CriteriaQueryCac protected boolean areDynamicNonLazyAssociationsChecked() { return false; - } - - public void testAliasToEntityMapNoProjectionList() { - reportSkip( "Transformers.ALIAS_TO_ENTITY_MAP with Criteria fails when try put in cache", - "Cache results using Transformers.ALIAS_TO_ENTITY_MAP with Criteria" ); - } - - public void testAliasToEntityMapNoProjectionListFailureExpected() throws Exception { - super.testAliasToEntityMapNoProjectionList(); - } - - public void testAliasToEntityMapNoProjectionMultiAndNullList() { - reportSkip( "Transformers.ALIAS_TO_ENTITY_MAP with Criteria fails when try put in cache", - "Cache results using Transformers.ALIAS_TO_ENTITY_MAP with Criteria" ); - } - public void testAliasToEntityMapNoProjectionMultiAndNullListFailureExpected() throws Exception { - super.testAliasToEntityMapNoProjectionMultiAndNullList(); - } - - public void testAliasToEntityMapOneProjectionList() { - reportSkip( "Transformers.ALIAS_TO_ENTITY_MAP with Criteria fails when try put in cache", - "Cache results using Transformers.ALIAS_TO_ENTITY_MAP with Criteria" ); - } - public void testAliasToEntityMapOneProjectionListFailureExpected() throws Exception { - super.testAliasToEntityMapOneProjectionList(); - } - - public void testAliasToEntityMapMultiProjectionList() { - reportSkip( "Transformers.ALIAS_TO_ENTITY_MAP with Criteria fails when try put in cache", - "Cache results using Transformers.ALIAS_TO_ENTITY_MAP with Criteria" ); - } - public void testAliasToEntityMapMultiProjectionListFailureExpected() throws Exception { - super.testAliasToEntityMapMultiProjectionList(); - } - - public void testAliasToEntityMapMultiProjectionWithNullAliasList() { - reportSkip( "Transformers.ALIAS_TO_ENTITY_MAP with Criteria fails when try put in cache", - "Cache results using Transformers.ALIAS_TO_ENTITY_MAP with Criteria" ); - } - public void testAliasToEntityMapMultiProjectionWithNullAliasListFailureExpected() throws Exception { - super.testAliasToEntityMapMultiProjectionWithNullAliasList(); - } - - public void testAliasToEntityMapMultiAggregatedPropProjectionSingleResult() { - reportSkip( "Transformers.ALIAS_TO_ENTITY_MAP with Criteria fails when try put in cache", - "Cache results using Transformers.ALIAS_TO_ENTITY_MAP with Criteria" ); - } - public void testAliasToEntityMapMultiAggregatedPropProjectionSingleResultFailureExpected() throws Exception { - super.testAliasToEntityMapMultiAggregatedPropProjectionSingleResult(); - } - - public void testAliasToBeanDtoOneArgList() { - reportSkip( "Transformers.aliasToBean with Criteria fails when try put in cache", - "Cache results using Transformers.aliasToBean with Criteria" ); - } - public void testAliasToBeanDtoOneArgListFailureExpected() throws Exception { - super.testAliasToBeanDtoOneArgList(); - } - - public void testAliasToBeanDtoMultiArgList() { - reportSkip( "Transformers.aliasToBean with Criteria fails when try put in cache", - "Cache results using Transformers.aliasToBean with Criteria" ); - } - public void testAliasToBeanDtoMultiArgListFailureExpected() throws Exception { - super.testAliasToBeanDtoMultiArgList(); - } - - public void testAliasToBeanDtoLiteralArgList() { - reportSkip( "Transformers.aliasToBean with Criteria fails when try put in cache", - "Cache results using Transformers.aliasToBean with Criteria" ); - } - public void testAliasToBeanDtoLiteralArgListFailureExpected() throws Exception { - super.testAliasToBeanDtoLiteralArgList(); - } - - public void testAliasToBeanDtoWithNullAliasList() { - reportSkip( "Transformers.aliasToBean with Criteria fails when try put in cache", - "Cache results using Transformers.aliasToBean with Criteria" ); - } - public void testAliasToBeanDtoWithNullAliasListFailureExpected() throws Exception { - super.testAliasToBeanDtoWithNullAliasList(); - } - - public void testOneSelectNewList() { - reportSkip( "Transformers.aliasToBean with Criteria fails when try put in cache", - "Cache results using Transformers.aliasToBean with Criteria" ); - } - public void testOneSelectNewListFailureExpected() throws Exception { - super.testOneSelectNewList(); - } - - public void testMultiSelectNewList() { - reportSkip( "Transformers.aliasToBean with Criteria fails when try put in cache", - "Cache results using Transformers.aliasToBean with Criteria" ); - } - public void testMultiSelectNewListFailureExpected() throws Exception { - super.testMultiSelectNewList(); - } - - public void testMultiSelectNewWithLiteralList() { - reportSkip( "Transformers.aliasToBean with Criteria fails when try put in cache", - "Cache results using Transformers.aliasToBean with Criteria" ); - } - public void testMultiSelectNewWithLiteralListFailureExpected() throws Exception { - super.testMultiSelectNewWithLiteralList(); - } - - public void testMultiSelectNewListList() { - reportSkip( "Transformers.aliasToBean with Criteria fails when try put in cache", - "Cache results using Transformers.aliasToBean with Criteria" ); - } - public void testMultiSelectNewListListFailureExpected() throws Exception { - super.testMultiSelectNewListList(); - } - - public void testMultiSelectNewMapList() { - reportSkip( "Transformers.aliasToBean with Criteria fails when try put in cache", - "Cache results using Transformers.aliasToBean with Criteria" ); - } - public void testMultiSelectNewMapListFailureExpected() throws Exception { - super.testMultiSelectNewMapList(); - } - - public void testSelectNewEntityConstructorList() { - reportSkip( "Transformers.aliasToBean with Criteria fails when try put in cache", - "Cache results using Transformers.aliasToBean with Criteria" ); - } - public void testSelectNewEntityConstructorListFailureExpected() throws Exception { - super.testMultiSelectNewMapList(); } } diff --git a/testsuite/src/test/java/org/hibernate/test/querycache/HqlQueryCacheIgnoreResultTransformerTest.java b/testsuite/src/test/java/org/hibernate/test/querycache/HqlQueryCacheIgnoreResultTransformerTest.java index a61cb896a6..adb3987baa 100644 --- a/testsuite/src/test/java/org/hibernate/test/querycache/HqlQueryCacheIgnoreResultTransformerTest.java +++ b/testsuite/src/test/java/org/hibernate/test/querycache/HqlQueryCacheIgnoreResultTransformerTest.java @@ -58,7 +58,6 @@ public class HqlQueryCacheIgnoreResultTransformerTest extends AbstractQueryCache 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(); } @@ -66,8 +65,23 @@ public class HqlQueryCacheIgnoreResultTransformerTest extends AbstractQueryCache 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(); + } + }