Merge branch 'master' of github.com:hibernate/hibernate-core into HHH-5623
This commit is contained in:
commit
7b521af146
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue