Merge branch 'master' of github.com:hibernate/hibernate-core into HHH-5623

This commit is contained in:
Steve Ebersole 2010-10-08 14:42:26 -05:00
commit 7b521af146
25 changed files with 1960 additions and 621 deletions

View File

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

View File

@ -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<returnTypes.length; i++ ) {
returnTypeInfo.append( "typename=" )
.append( returnTypes[ i ].getName() )
.append(" class=" )
.append( returnTypes[ i ].getReturnedClass().getName() ).append(' ');
}
log.trace( " returnTypeInfo="+returnTypeInfo );
}
}
private static void logCachedResultRowDetails(Type[] returnTypes, Object result) {
if ( ! log.isTraceEnabled() ) {
return;
}
logCachedResultRowDetails(
returnTypes,
( result instanceof Object[] ? ( Object[] ) result : new Object[] { result } )
);
}
private static void logCachedResultRowDetails(Type[] returnTypes, Object[] tuple) {
if ( ! log.isTraceEnabled() ) {
return;
}
if ( tuple == null ) {
log.trace( " tuple is null; returnTypes is "+( returnTypes == null ? "null" : "Type["+returnTypes.length+"]" ) );
if ( returnTypes != null && returnTypes.length > 1 ) {
log.trace( "unexpected result tuple! "+
"tuple is null; should be Object["+returnTypes.length+"]!" );
}
}
else {
if ( returnTypes == null || returnTypes.length == 0 ) {
log.trace( "unexpected result tuple! "+
"tuple is non-null; returnTypes is "+( returnTypes == null ? "null" : "empty" ) );
}
log.trace( " tuple is Object["+tuple.length+
"]; returnTypes is Type["+returnTypes.length+"]" );
if ( tuple.length != returnTypes.length ) {
log.trace( "unexpected tuple length! transformer="+
" expected="+returnTypes.length+
" got="+tuple.length );
}
else {
for ( int j = 0; j < tuple.length; j++ ) {
if ( tuple[ j ] != null && ! returnTypes[ j ].getReturnedClass().isInstance( tuple[ j ] ) ) {
log.trace( "unexpected tuple value type! transformer="+
" expected="+returnTypes[ j ].getReturnedClass().getName()+
" got="+tuple[ j ].getClass().getName() );
}
}
}
}
}
}

View File

@ -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;
resultRow = toResultRow( row );
}
}
else if ( holderClass == null ) {
return row.length == 1 ? row[0] : row;
}
else {
return row;
}
return resultRow;
}
protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {

View File

@ -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 );
@ -1025,13 +1069,20 @@ public abstract class Loader {
/**
* 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,20 +2346,7 @@ 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" ) );
@ -2316,7 +2365,7 @@ public abstract class Loader {
);
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
boolean put = queryCache.put(
key,
key.getResultTransformer().getCachedResultTypes( resultTypes ),
result,
queryParameters.isNaturalKeyLookup(),
session
);
}
// 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 );
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<returnTypes.length; i++ ) {
returnTypeNames.append( returnTypes[ i ].getName() ).append(' ');
returnClassNames.append( returnTypes[ i ].getReturnedClass() ).append(' ');
}
log.trace( "transformer="+( resultTransformer == null ? "null" : resultTransformer.getClass().getName() )+
" returnTypes=[ "+returnTypeNames+"]"+" returnClasses=[ "+returnClassNames+"]" );
}
if ( result != null && result.size() != 0 ) {
for ( Iterator it = result.iterator(); it.hasNext(); ) {
Object value = it.next();
if ( value == null ) {
log.trace( "transformer="+( resultTransformer == null ? "null" : resultTransformer.getClass().getName() )+
" value is null; returnTypes is "+( returnTypes == null ? "null" : "Type["+returnTypes.length+"]" ) );
if ( returnTypes != null && returnTypes.length > 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(

View File

@ -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,
@ -97,8 +106,6 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker {
querySpaces = translator.getQuerySpaces();
if ( translator.hasProjection() ) {
resultTypes = translator.getProjectedTypes();
initProjection(
translator.getSelect(),
translator.getWhereCondition(),
@ -106,16 +113,21 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker {
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 );
}
}
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) {
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 );

View File

@ -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() {

View File

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

View File

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

View File

@ -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 );
}
else {
row = new Object[queryCols];
resultRow = new Object[queryCols];
for ( int i = 0; i < queryCols; i++ ) {
row[i] = queryReturnTypes[i].nullSafeGet( rs, scalarColumns[i], session, null );
resultRow[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 {

View File

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

View File

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

View File

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

View File

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

View File

@ -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();

View File

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

View File

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

View File

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

View File

@ -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();

View File

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

View File

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

View File

@ -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();

View File

@ -49,9 +49,13 @@ public class CriteriaQueryCacheIgnoreResultTransformerTest extends AbstractQuery
protected void runTest(HqlExecutor hqlExecutor, CriteriaExecutor criteriaExecutor, ResultChecker checker, boolean isSingleResult)
throws Exception {
createData();
try {
if ( criteriaExecutor != null ) {
runTest( criteriaExecutor, checker, isSingleResult );
}
}
finally {
deleteData();
}
}
}

View File

@ -49,133 +49,4 @@ 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();
}
}

View File

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