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.RowSelection;
|
||||||
import org.hibernate.engine.SessionImplementor;
|
import org.hibernate.engine.SessionImplementor;
|
||||||
import org.hibernate.engine.TypedValue;
|
import org.hibernate.engine.TypedValue;
|
||||||
|
import org.hibernate.transform.CacheableResultTransformer;
|
||||||
import org.hibernate.transform.ResultTransformer;
|
import org.hibernate.transform.ResultTransformer;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
import org.hibernate.util.EqualsHelper;
|
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
|
// the user provided resulttransformer, not the one used with "select new". Here to avoid mangling
|
||||||
// transformed/non-transformed results.
|
// 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
|
* 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,
|
QueryParameters queryParameters,
|
||||||
Set filterKeys,
|
Set filterKeys,
|
||||||
SessionImplementor session,
|
SessionImplementor session,
|
||||||
ResultTransformer customTransformer) {
|
CacheableResultTransformer customTransformer) {
|
||||||
// disassemble positional parameters
|
// disassemble positional parameters
|
||||||
final int positionalParameterCount = queryParameters.getPositionalParameterTypes().length;
|
final int positionalParameterCount = queryParameters.getPositionalParameterTypes().length;
|
||||||
final Type[] types = new Type[positionalParameterCount];
|
final Type[] types = new Type[positionalParameterCount];
|
||||||
|
@ -163,7 +164,7 @@ public class QueryKey implements Serializable {
|
||||||
Integer maxRows,
|
Integer maxRows,
|
||||||
Set filterKeys,
|
Set filterKeys,
|
||||||
EntityMode entityMode,
|
EntityMode entityMode,
|
||||||
ResultTransformer customTransformer) {
|
CacheableResultTransformer customTransformer) {
|
||||||
this.sqlQueryString = sqlQueryString;
|
this.sqlQueryString = sqlQueryString;
|
||||||
this.positionalParameterTypes = positionalParameterTypes;
|
this.positionalParameterTypes = positionalParameterTypes;
|
||||||
this.positionalParameterValues = positionalParameterValues;
|
this.positionalParameterValues = positionalParameterValues;
|
||||||
|
@ -176,7 +177,7 @@ public class QueryKey implements Serializable {
|
||||||
this.hashCode = generateHashCode();
|
this.hashCode = generateHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResultTransformer getResultTransformer() {
|
public CacheableResultTransformer getResultTransformer() {
|
||||||
return customTransformer;
|
return customTransformer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,9 @@ public class StandardQueryCache implements QueryCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
List cacheable = new ArrayList( result.size() + 1 );
|
List cacheable = new ArrayList( result.size() + 1 );
|
||||||
|
if ( log.isTraceEnabled() ) {
|
||||||
|
logCachedResultDetails( key, null, returnTypes, cacheable );
|
||||||
|
}
|
||||||
cacheable.add( ts );
|
cacheable.add( ts );
|
||||||
for ( Object aResult : result ) {
|
for ( Object aResult : result ) {
|
||||||
if ( returnTypes.length == 1 ) {
|
if ( returnTypes.length == 1 ) {
|
||||||
|
@ -107,6 +110,9 @@ public class StandardQueryCache implements QueryCache {
|
||||||
TypeHelper.disassemble( (Object[]) aResult, returnTypes, null, session, null )
|
TypeHelper.disassemble( (Object[]) aResult, returnTypes, null, session, null )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if ( log.isTraceEnabled() ) {
|
||||||
|
logCachedResultRowDetails( returnTypes, aResult );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheRegion.put( key, cacheable );
|
cacheRegion.put( key, cacheable );
|
||||||
|
@ -126,6 +132,10 @@ public class StandardQueryCache implements QueryCache {
|
||||||
}
|
}
|
||||||
|
|
||||||
List cacheable = ( List ) cacheRegion.get( key );
|
List cacheable = ( List ) cacheRegion.get( key );
|
||||||
|
if ( log.isTraceEnabled() ) {
|
||||||
|
logCachedResultDetails( key, spaces, returnTypes, cacheable );
|
||||||
|
}
|
||||||
|
|
||||||
if ( cacheable == null ) {
|
if ( cacheable == null ) {
|
||||||
log.debug( "query results were not found in cache" );
|
log.debug( "query results were not found in cache" );
|
||||||
return null;
|
return null;
|
||||||
|
@ -157,6 +167,9 @@ public class StandardQueryCache implements QueryCache {
|
||||||
TypeHelper.assemble( ( Serializable[] ) cacheable.get( i ), returnTypes, session, null )
|
TypeHelper.assemble( ( Serializable[] ) cacheable.get( i ), returnTypes, session, null )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if ( log.isTraceEnabled() ) {
|
||||||
|
logCachedResultRowDetails( returnTypes, result.get( i - 1 ));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch ( RuntimeException ex ) {
|
catch ( RuntimeException ex ) {
|
||||||
if ( isNaturalKeyLookup &&
|
if ( isNaturalKeyLookup &&
|
||||||
|
@ -202,4 +215,70 @@ public class StandardQueryCache implements QueryCache {
|
||||||
return "StandardQueryCache(" + cacheRegion.getName() + ')';
|
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.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
@ -71,7 +72,6 @@ import org.hibernate.transform.ResultTransformer;
|
||||||
import org.hibernate.type.AssociationType;
|
import org.hibernate.type.AssociationType;
|
||||||
import org.hibernate.type.EntityType;
|
import org.hibernate.type.EntityType;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
import org.hibernate.type.TypeFactory;
|
|
||||||
import org.hibernate.util.ArrayHelper;
|
import org.hibernate.util.ArrayHelper;
|
||||||
import org.hibernate.util.ReflectHelper;
|
import org.hibernate.util.ReflectHelper;
|
||||||
import org.hibernate.util.StringHelper;
|
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...");
|
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) {
|
protected ResultTransformer resolveResultTransformer(ResultTransformer resultTransformer) {
|
||||||
return HolderInstantiator.resolveClassicResultTransformer(
|
return HolderInstantiator.resolveClassicResultTransformer(
|
||||||
holderConstructor,
|
holderConstructor,
|
||||||
|
@ -992,27 +1002,28 @@ public class QueryTranslatorImpl extends BasicLoader implements FilterTranslator
|
||||||
|
|
||||||
protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session)
|
protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session)
|
||||||
throws SQLException, HibernateException {
|
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 ) {
|
if ( hasScalars ) {
|
||||||
String[][] scalarColumns = getColumnNames();
|
String[][] scalarColumns = getColumnNames();
|
||||||
int queryCols = returnTypes.length;
|
int queryCols = returnTypes.length;
|
||||||
if ( holderClass == null && queryCols == 1 ) {
|
resultRow = new Object[queryCols];
|
||||||
return returnTypes[0].nullSafeGet( rs, scalarColumns[0], session, null );
|
for ( int i = 0; i < queryCols; i++ ) {
|
||||||
|
resultRow[i] = returnTypes[i].nullSafeGet( rs, scalarColumns[i], session, null );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
row = new Object[queryCols];
|
resultRow = toResultRow( row );
|
||||||
for ( int i = 0; i < queryCols; i++ )
|
|
||||||
row[i] = returnTypes[i].nullSafeGet( rs, scalarColumns[i], session, null );
|
|
||||||
return row;
|
|
||||||
}
|
}
|
||||||
}
|
return resultRow;
|
||||||
else if ( holderClass == null ) {
|
|
||||||
return row.length == 1 ? row[0] : row;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {
|
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.persister.entity.UniqueKeyLoadable;
|
||||||
import org.hibernate.pretty.MessageHelper;
|
import org.hibernate.pretty.MessageHelper;
|
||||||
import org.hibernate.proxy.HibernateProxy;
|
import org.hibernate.proxy.HibernateProxy;
|
||||||
|
import org.hibernate.transform.CacheableResultTransformer;
|
||||||
import org.hibernate.transform.ResultTransformer;
|
import org.hibernate.transform.ResultTransformer;
|
||||||
import org.hibernate.type.AssociationType;
|
import org.hibernate.type.AssociationType;
|
||||||
import org.hibernate.type.EntityType;
|
import org.hibernate.type.EntityType;
|
||||||
|
@ -255,6 +256,20 @@ public abstract class Loader {
|
||||||
final SessionImplementor session,
|
final SessionImplementor session,
|
||||||
final QueryParameters queryParameters,
|
final QueryParameters queryParameters,
|
||||||
final boolean returnProxies) throws HibernateException, SQLException {
|
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();
|
final PersistenceContext persistenceContext = session.getPersistenceContext();
|
||||||
boolean defaultReadOnlyOrig = persistenceContext.isDefaultReadOnly();
|
boolean defaultReadOnlyOrig = persistenceContext.isDefaultReadOnly();
|
||||||
if ( queryParameters.isReadOnlyInitialized() ) {
|
if ( queryParameters.isReadOnlyInitialized() ) {
|
||||||
|
@ -271,7 +286,7 @@ public abstract class Loader {
|
||||||
List result;
|
List result;
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
result = doQuery( session, queryParameters, returnProxies );
|
result = doQuery( session, queryParameters, returnProxies, forcedResultTransformer );
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
persistenceContext.afterLoad();
|
persistenceContext.afterLoad();
|
||||||
|
@ -600,7 +615,29 @@ public abstract class Loader {
|
||||||
final List hydratedObjects,
|
final List hydratedObjects,
|
||||||
final EntityKey[] keys,
|
final EntityKey[] keys,
|
||||||
boolean returnProxies) throws SQLException, HibernateException {
|
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 Loadable[] persisters = getEntityPersisters();
|
||||||
final int entitySpan = persisters.length;
|
final int entitySpan = persisters.length;
|
||||||
extractKeysFromResultSet( persisters, queryParameters, resultSet, session, keys, lockModesArray, hydratedObjects );
|
extractKeysFromResultSet( persisters, queryParameters, resultSet, session, keys, lockModesArray, hydratedObjects );
|
||||||
|
@ -636,8 +673,13 @@ public abstract class Loader {
|
||||||
|
|
||||||
applyPostLoadLocks( row, lockModesArray, session );
|
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(
|
protected void extractKeysFromResultSet(
|
||||||
|
@ -788,7 +830,8 @@ public abstract class Loader {
|
||||||
private List doQuery(
|
private List doQuery(
|
||||||
final SessionImplementor session,
|
final SessionImplementor session,
|
||||||
final QueryParameters queryParameters,
|
final QueryParameters queryParameters,
|
||||||
final boolean returnProxies) throws SQLException, HibernateException {
|
final boolean returnProxies,
|
||||||
|
final ResultTransformer forcedResultTransformer) throws SQLException, HibernateException {
|
||||||
|
|
||||||
final RowSelection selection = queryParameters.getRowSelection();
|
final RowSelection selection = queryParameters.getRowSelection();
|
||||||
final int maxRows = hasMaxRows( selection ) ?
|
final int maxRows = hasMaxRows( selection ) ?
|
||||||
|
@ -834,7 +877,8 @@ public abstract class Loader {
|
||||||
optionalObjectKey,
|
optionalObjectKey,
|
||||||
hydratedObjects,
|
hydratedObjects,
|
||||||
keys,
|
keys,
|
||||||
returnProxies
|
returnProxies,
|
||||||
|
forcedResultTransformer
|
||||||
);
|
);
|
||||||
results.add( result );
|
results.add( result );
|
||||||
|
|
||||||
|
@ -1025,13 +1069,20 @@ public abstract class Loader {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Are rows transformed immediately after being read from the ResultSet?
|
* Are rows transformed immediately after being read from the ResultSet?
|
||||||
* @param transformer, the specified transformer
|
|
||||||
* @return true, if getResultColumnOrRow() transforms the results; false, otherwise
|
* @return true, if getResultColumnOrRow() transforms the results; false, otherwise
|
||||||
*/
|
*/
|
||||||
protected boolean areResultSetRowsTransformedImmediately(ResultTransformer transformer) {
|
protected boolean areResultSetRowsTransformedImmediately() {
|
||||||
return false;
|
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.
|
* Get the actual object that is returned in the user-visible result list.
|
||||||
* This empty implementation merely returns its first argument. This is
|
* This empty implementation merely returns its first argument. This is
|
||||||
|
@ -1042,6 +1093,17 @@ public abstract class Loader {
|
||||||
return row;
|
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
|
* 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
|
* 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() );
|
QueryCache queryCache = factory.getQueryCache( queryParameters.getCacheRegion() );
|
||||||
|
|
||||||
Set filterKeys = FilterKey.createFilterKeys(
|
QueryKey key = generateQueryKey( session, queryParameters );
|
||||||
session.getLoadQueryInfluencers().getEnabledFilters(),
|
|
||||||
session.getEntityMode()
|
|
||||||
);
|
|
||||||
QueryKey key = QueryKey.generateQueryKey(
|
|
||||||
getSQLString(),
|
|
||||||
queryParameters,
|
|
||||||
filterKeys,
|
|
||||||
session,
|
|
||||||
( areResultSetRowsTransformedImmediately( queryParameters.getResultTransformer() ) ?
|
|
||||||
queryParameters.getResultTransformer() :
|
|
||||||
null
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( querySpaces == null || querySpaces.size() == 0 ) {
|
if ( querySpaces == null || querySpaces.size() == 0 ) {
|
||||||
log.trace( "unexpected querySpaces is "+( querySpaces == null ? "null" : "empty" ) );
|
log.trace( "unexpected querySpaces is "+( querySpaces == null ? "null" : "empty" ) );
|
||||||
|
@ -2316,7 +2365,7 @@ public abstract class Loader {
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( result == null ) {
|
if ( result == null ) {
|
||||||
result = doList( session, queryParameters );
|
result = doList( session, queryParameters, key.getResultTransformer() );
|
||||||
|
|
||||||
putResultInQueryCache(
|
putResultInQueryCache(
|
||||||
session,
|
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() );
|
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(
|
private List getResultFromQueryCache(
|
||||||
final SessionImplementor session,
|
final SessionImplementor session,
|
||||||
final QueryParameters queryParameters,
|
final QueryParameters queryParameters,
|
||||||
|
@ -2358,33 +2445,18 @@ public abstract class Loader {
|
||||||
queryParameters.setReadOnly( persistenceContext.isDefaultReadOnly() );
|
queryParameters.setReadOnly( persistenceContext.isDefaultReadOnly() );
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
result = queryCache.get( key, resultTypes, isImmutableNaturalKeyLookup, querySpaces, session );
|
result = queryCache.get(
|
||||||
logCachedResultDetails(
|
key,
|
||||||
key.getResultTransformer(),
|
key.getResultTransformer().getCachedResultTypes( resultTypes ),
|
||||||
resultTypes,
|
isImmutableNaturalKeyLookup,
|
||||||
result
|
querySpaces,
|
||||||
|
session
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
persistenceContext.setDefaultReadOnly( defaultReadOnlyOrig );
|
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 ( factory.getStatistics().isStatisticsEnabled() ) {
|
||||||
if ( result == null ) {
|
if ( result == null ) {
|
||||||
factory.getStatisticsImplementor()
|
factory.getStatisticsImplementor()
|
||||||
|
@ -2408,30 +2480,13 @@ public abstract class Loader {
|
||||||
final QueryKey key,
|
final QueryKey key,
|
||||||
final List result) {
|
final List result) {
|
||||||
if ( session.getCacheMode().isPutEnabled() ) {
|
if ( session.getCacheMode().isPutEnabled() ) {
|
||||||
if ( log.isTraceEnabled() ) {
|
boolean put = queryCache.put(
|
||||||
logCachedResultDetails(
|
key,
|
||||||
key.getResultTransformer(),
|
key.getResultTransformer().getCachedResultTypes( resultTypes ),
|
||||||
resultTypes,
|
result,
|
||||||
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() ) {
|
if ( put && factory.getStatistics().isStatisticsEnabled() ) {
|
||||||
factory.getStatisticsImplementor()
|
factory.getStatisticsImplementor()
|
||||||
.queryCachePut( getQueryIdentifier(), queryCache.getRegion().getName() );
|
.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
|
* Actually execute a query, ignoring the query cache
|
||||||
*/
|
*/
|
||||||
|
|
||||||
protected List doList(final SessionImplementor session, final QueryParameters queryParameters)
|
protected List doList(final SessionImplementor session, final QueryParameters queryParameters)
|
||||||
throws HibernateException {
|
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();
|
final boolean stats = getFactory().getStatistics().isStatisticsEnabled();
|
||||||
long startTime = 0;
|
long startTime = 0;
|
||||||
|
@ -2530,7 +2514,7 @@ public abstract class Loader {
|
||||||
|
|
||||||
List result;
|
List result;
|
||||||
try {
|
try {
|
||||||
result = doQueryAndInitializeNonLazyCollections( session, queryParameters, true );
|
result = doQueryAndInitializeNonLazyCollections( session, queryParameters, true, forcedResultTransformer );
|
||||||
}
|
}
|
||||||
catch ( SQLException sqle ) {
|
catch ( SQLException sqle ) {
|
||||||
throw JDBCExceptionHelper.convert(
|
throw JDBCExceptionHelper.convert(
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
package org.hibernate.loader.criteria;
|
package org.hibernate.loader.criteria;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -59,10 +60,14 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker {
|
||||||
private final CriteriaQueryTranslator translator;
|
private final CriteriaQueryTranslator translator;
|
||||||
private final Set querySpaces;
|
private final Set querySpaces;
|
||||||
private final Type[] resultTypes;
|
private final Type[] resultTypes;
|
||||||
|
private final boolean[] includeInResultRow;
|
||||||
|
|
||||||
//the user visible aliases, which are unknown to the superclass,
|
//the user visible aliases, which are unknown to the superclass,
|
||||||
//these are not the actual "physical" SQL aliases
|
//these are not the actual "physical" SQL aliases
|
||||||
private final String[] userAliases;
|
private final String[] userAliases;
|
||||||
private final List userAliasList = new ArrayList();
|
private final List userAliasList = new ArrayList();
|
||||||
|
private final List resultTypeList = new ArrayList();
|
||||||
|
private final List includeInResultRowList = new ArrayList();
|
||||||
|
|
||||||
public Type[] getResultTypes() {
|
public Type[] getResultTypes() {
|
||||||
return resultTypes;
|
return resultTypes;
|
||||||
|
@ -72,6 +77,10 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker {
|
||||||
return userAliases;
|
return userAliases;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean[] includeInResultRow() {
|
||||||
|
return includeInResultRow;
|
||||||
|
}
|
||||||
|
|
||||||
public CriteriaJoinWalker(
|
public CriteriaJoinWalker(
|
||||||
final OuterJoinLoadable persister,
|
final OuterJoinLoadable persister,
|
||||||
final CriteriaQueryTranslator translator,
|
final CriteriaQueryTranslator translator,
|
||||||
|
@ -97,8 +106,6 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker {
|
||||||
querySpaces = translator.getQuerySpaces();
|
querySpaces = translator.getQuerySpaces();
|
||||||
|
|
||||||
if ( translator.hasProjection() ) {
|
if ( translator.hasProjection() ) {
|
||||||
resultTypes = translator.getProjectedTypes();
|
|
||||||
|
|
||||||
initProjection(
|
initProjection(
|
||||||
translator.getSelect(),
|
translator.getSelect(),
|
||||||
translator.getWhereCondition(),
|
translator.getWhereCondition(),
|
||||||
|
@ -106,16 +113,21 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker {
|
||||||
translator.getGroupBy(),
|
translator.getGroupBy(),
|
||||||
LockOptions.NONE
|
LockOptions.NONE
|
||||||
);
|
);
|
||||||
|
resultTypes = translator.getProjectedTypes();
|
||||||
|
userAliases = translator.getProjectedAliases();
|
||||||
|
includeInResultRow = new boolean[ resultTypes.length ];
|
||||||
|
Arrays.fill( includeInResultRow, true );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
resultTypes = new Type[] { factory.getTypeResolver().getTypeFactory().manyToOne( persister.getEntityName() ) };
|
|
||||||
|
|
||||||
initAll( translator.getWhereCondition(), translator.getOrderBy(), LockOptions.NONE );
|
initAll( translator.getWhereCondition(), translator.getOrderBy(), LockOptions.NONE );
|
||||||
}
|
// root entity comes last
|
||||||
|
|
||||||
userAliasList.add( criteria.getAlias() ); //root entity comes *last*
|
userAliasList.add( criteria.getAlias() ); //root entity comes *last*
|
||||||
|
resultTypeList.add( translator.getResultType( criteria ) );
|
||||||
|
includeInResultRowList.add( true );
|
||||||
userAliases = ArrayHelper.toStringArray( userAliasList );
|
userAliases = ArrayHelper.toStringArray( userAliasList );
|
||||||
|
resultTypes = ArrayHelper.toTypeArray( resultTypeList );
|
||||||
|
includeInResultRow = ArrayHelper.toBooleanArray( includeInResultRowList );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int getJoinType(
|
protected int getJoinType(
|
||||||
|
@ -208,15 +220,24 @@ public class CriteriaJoinWalker extends AbstractEntityJoinWalker {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String generateTableAlias(int n, PropertyPath path, Joinable joinable) {
|
protected String generateTableAlias(int n, PropertyPath path, Joinable joinable) {
|
||||||
|
// TODO: deal with side-effects (changes to includeInSelectList, userAliasList, resultTypeList)!!!
|
||||||
if ( joinable.consumesEntityAlias() ) {
|
if ( joinable.consumesEntityAlias() ) {
|
||||||
final Criteria subcriteria = translator.getCriteria( path.getFullPath() );
|
final Criteria subcriteria = translator.getCriteria( path.getFullPath() );
|
||||||
String sqlAlias = subcriteria==null ? null : translator.getSQLAlias(subcriteria);
|
String sqlAlias = subcriteria==null ? null : translator.getSQLAlias(subcriteria);
|
||||||
if (sqlAlias!=null) {
|
if (sqlAlias!=null) {
|
||||||
|
if ( ! translator.hasProjection() ) {
|
||||||
|
includeInResultRowList.add( subcriteria.getAlias() != null );
|
||||||
|
if ( subcriteria.getAlias() != null ) {
|
||||||
userAliasList.add( subcriteria.getAlias() ); //alias may be null
|
userAliasList.add( subcriteria.getAlias() ); //alias may be null
|
||||||
|
resultTypeList.add( translator.getResultType( subcriteria ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
return sqlAlias; //EARLY EXIT
|
return sqlAlias; //EARLY EXIT
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
userAliasList.add(null);
|
if ( ! translator.hasProjection() ) {
|
||||||
|
includeInResultRowList.add( false );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.generateTableAlias( n + translator.getSQLAliasCount(), path, joinable );
|
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,
|
//the user visible aliases, which are unknown to the superclass,
|
||||||
//these are not the actual "physical" SQL aliases
|
//these are not the actual "physical" SQL aliases
|
||||||
private final String[] userAliases;
|
private final String[] userAliases;
|
||||||
|
private final boolean[] includeInResultRow;
|
||||||
|
private final int resultRowLength;
|
||||||
|
|
||||||
public CriteriaLoader(
|
public CriteriaLoader(
|
||||||
final OuterJoinLoadable persister,
|
final OuterJoinLoadable persister,
|
||||||
|
@ -102,6 +104,8 @@ public class CriteriaLoader extends OuterJoinLoader {
|
||||||
|
|
||||||
userAliases = walker.getUserAliases();
|
userAliases = walker.getUserAliases();
|
||||||
resultTypes = walker.getResultTypes();
|
resultTypes = walker.getResultTypes();
|
||||||
|
includeInResultRow = walker.includeInResultRow();
|
||||||
|
resultRowLength = ArrayHelper.countTrue( includeInResultRow );
|
||||||
|
|
||||||
postInstantiate();
|
postInstantiate();
|
||||||
|
|
||||||
|
@ -120,20 +124,33 @@ public class CriteriaLoader extends OuterJoinLoader {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String[] getResultRowAliases() {
|
||||||
|
return userAliases;
|
||||||
|
}
|
||||||
|
|
||||||
protected ResultTransformer resolveResultTransformer(ResultTransformer resultTransformer) {
|
protected ResultTransformer resolveResultTransformer(ResultTransformer resultTransformer) {
|
||||||
return translator.getRootCriteria().getResultTransformer();
|
return translator.getRootCriteria().getResultTransformer();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean areResultSetRowsTransformedImmediately( ResultTransformer transformer ) {
|
protected boolean areResultSetRowsTransformedImmediately() {
|
||||||
// comparing to null just in case there is no transformer
|
return true;
|
||||||
// (there should always be a result transformer;
|
}
|
||||||
return resolveResultTransformer( transformer ) != null;
|
|
||||||
|
protected boolean[] includeInResultRow() {
|
||||||
|
return includeInResultRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session)
|
protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session)
|
||||||
|
throws SQLException, HibernateException {
|
||||||
|
return resolveResultTransformer( transformer ).transformTuple(
|
||||||
|
getResultRow( row, rs, session),
|
||||||
|
getResultRowAliases()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object[] getResultRow(Object[] row, ResultSet rs, SessionImplementor session)
|
||||||
throws SQLException, HibernateException {
|
throws SQLException, HibernateException {
|
||||||
final Object[] result;
|
final Object[] result;
|
||||||
final String[] aliases;
|
|
||||||
if ( translator.hasProjection() ) {
|
if ( translator.hasProjection() ) {
|
||||||
Type[] types = translator.getProjectedTypes();
|
Type[] types = translator.getProjectedTypes();
|
||||||
result = new Object[types.length];
|
result = new Object[types.length];
|
||||||
|
@ -149,13 +166,25 @@ public class CriteriaLoader extends OuterJoinLoader {
|
||||||
}
|
}
|
||||||
pos += numColumns;
|
pos += numColumns;
|
||||||
}
|
}
|
||||||
aliases = translator.getProjectedAliases();
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result = row;
|
result = toResultRow( row );
|
||||||
aliases = userAliases;
|
}
|
||||||
|
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() {
|
public Set getQuerySpaces() {
|
||||||
|
|
|
@ -55,6 +55,8 @@ import org.hibernate.impl.CriteriaImpl;
|
||||||
import org.hibernate.persister.entity.Loadable;
|
import org.hibernate.persister.entity.Loadable;
|
||||||
import org.hibernate.persister.entity.PropertyMapping;
|
import org.hibernate.persister.entity.PropertyMapping;
|
||||||
import org.hibernate.persister.entity.Queryable;
|
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.AssociationType;
|
||||||
import org.hibernate.type.StringRepresentableType;
|
import org.hibernate.type.StringRepresentableType;
|
||||||
import org.hibernate.type.Type;
|
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() {
|
public Type[] getProjectedTypes() {
|
||||||
return rootCriteria.getProjection().getTypes( rootCriteria, this );
|
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.persister.entity.Queryable;
|
||||||
import org.hibernate.transform.ResultTransformer;
|
import org.hibernate.transform.ResultTransformer;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
import org.hibernate.type.TypeFactory;
|
|
||||||
import org.hibernate.type.EntityType;
|
import org.hibernate.type.EntityType;
|
||||||
import org.hibernate.type.CollectionType;
|
import org.hibernate.type.CollectionType;
|
||||||
import org.hibernate.util.ArrayHelper;
|
import org.hibernate.util.ArrayHelper;
|
||||||
|
@ -83,6 +82,8 @@ public class CustomLoader extends Loader {
|
||||||
|
|
||||||
private final LockMode[] lockModes;
|
private final LockMode[] lockModes;
|
||||||
|
|
||||||
|
private boolean[] includeInResultRow;
|
||||||
|
|
||||||
// private final String[] sqlAliases;
|
// private final String[] sqlAliases;
|
||||||
// private final String[] sqlAliasSuffixes;
|
// private final String[] sqlAliasSuffixes;
|
||||||
private final ResultRowProcessor rowProcessor;
|
private final ResultRowProcessor rowProcessor;
|
||||||
|
@ -94,7 +95,6 @@ public class CustomLoader extends Loader {
|
||||||
// this is only needed (afaict) for ResultTransformer processing...
|
// this is only needed (afaict) for ResultTransformer processing...
|
||||||
private String[] transformerAliases;
|
private String[] transformerAliases;
|
||||||
|
|
||||||
|
|
||||||
public CustomLoader(CustomQuery customQuery, SessionFactoryImplementor factory) {
|
public CustomLoader(CustomQuery customQuery, SessionFactoryImplementor factory) {
|
||||||
super( factory );
|
super( factory );
|
||||||
|
|
||||||
|
@ -118,6 +118,8 @@ public class CustomLoader extends Loader {
|
||||||
int returnableCounter = 0;
|
int returnableCounter = 0;
|
||||||
boolean hasScalars = false;
|
boolean hasScalars = false;
|
||||||
|
|
||||||
|
List includeInResultRowList = new ArrayList();
|
||||||
|
|
||||||
Iterator itr = customQuery.getCustomQueryReturns().iterator();
|
Iterator itr = customQuery.getCustomQueryReturns().iterator();
|
||||||
while ( itr.hasNext() ) {
|
while ( itr.hasNext() ) {
|
||||||
final Return rtn = ( Return ) itr.next();
|
final Return rtn = ( Return ) itr.next();
|
||||||
|
@ -131,6 +133,7 @@ public class CustomLoader extends Loader {
|
||||||
scalarRtn.getType()
|
scalarRtn.getType()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
includeInResultRowList.add( true );
|
||||||
hasScalars = true;
|
hasScalars = true;
|
||||||
}
|
}
|
||||||
else if ( rtn instanceof RootReturn ) {
|
else if ( rtn instanceof RootReturn ) {
|
||||||
|
@ -145,6 +148,7 @@ public class CustomLoader extends Loader {
|
||||||
specifiedAliases.add( rootRtn.getAlias() );
|
specifiedAliases.add( rootRtn.getAlias() );
|
||||||
entityAliases.add( rootRtn.getEntityAliases() );
|
entityAliases.add( rootRtn.getEntityAliases() );
|
||||||
ArrayHelper.addAll( querySpaces, persister.getQuerySpaces() );
|
ArrayHelper.addAll( querySpaces, persister.getQuerySpaces() );
|
||||||
|
includeInResultRowList.add( true );
|
||||||
}
|
}
|
||||||
else if ( rtn instanceof CollectionReturn ) {
|
else if ( rtn instanceof CollectionReturn ) {
|
||||||
CollectionReturn collRtn = ( CollectionReturn ) rtn;
|
CollectionReturn collRtn = ( CollectionReturn ) rtn;
|
||||||
|
@ -167,6 +171,7 @@ public class CustomLoader extends Loader {
|
||||||
entityAliases.add( collRtn.getElementEntityAliases() );
|
entityAliases.add( collRtn.getElementEntityAliases() );
|
||||||
ArrayHelper.addAll( querySpaces, elementPersister.getQuerySpaces() );
|
ArrayHelper.addAll( querySpaces, elementPersister.getQuerySpaces() );
|
||||||
}
|
}
|
||||||
|
includeInResultRowList.add( true );
|
||||||
}
|
}
|
||||||
else if ( rtn instanceof EntityFetchReturn ) {
|
else if ( rtn instanceof EntityFetchReturn ) {
|
||||||
EntityFetchReturn fetchRtn = ( EntityFetchReturn ) rtn;
|
EntityFetchReturn fetchRtn = ( EntityFetchReturn ) rtn;
|
||||||
|
@ -183,6 +188,7 @@ public class CustomLoader extends Loader {
|
||||||
specifiedAliases.add( fetchRtn.getAlias() );
|
specifiedAliases.add( fetchRtn.getAlias() );
|
||||||
entityAliases.add( fetchRtn.getEntityAliases() );
|
entityAliases.add( fetchRtn.getEntityAliases() );
|
||||||
ArrayHelper.addAll( querySpaces, persister.getQuerySpaces() );
|
ArrayHelper.addAll( querySpaces, persister.getQuerySpaces() );
|
||||||
|
includeInResultRowList.add( false );
|
||||||
}
|
}
|
||||||
else if ( rtn instanceof CollectionFetchReturn ) {
|
else if ( rtn instanceof CollectionFetchReturn ) {
|
||||||
CollectionFetchReturn fetchRtn = ( CollectionFetchReturn ) rtn;
|
CollectionFetchReturn fetchRtn = ( CollectionFetchReturn ) rtn;
|
||||||
|
@ -206,6 +212,7 @@ public class CustomLoader extends Loader {
|
||||||
entityAliases.add( fetchRtn.getElementEntityAliases() );
|
entityAliases.add( fetchRtn.getElementEntityAliases() );
|
||||||
ArrayHelper.addAll( querySpaces, elementPersister.getQuerySpaces() );
|
ArrayHelper.addAll( querySpaces, elementPersister.getQuerySpaces() );
|
||||||
}
|
}
|
||||||
|
includeInResultRowList.add( false );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new HibernateException( "unexpected custom query return type : " + rtn.getClass().getName() );
|
throw new HibernateException( "unexpected custom query return type : " + rtn.getClass().getName() );
|
||||||
|
@ -244,6 +251,8 @@ public class CustomLoader extends Loader {
|
||||||
hasScalars,
|
hasScalars,
|
||||||
( ResultColumnProcessor[] ) resultColumnProcessors.toArray( new ResultColumnProcessor[ resultColumnProcessors.size() ] )
|
( ResultColumnProcessor[] ) resultColumnProcessors.toArray( new ResultColumnProcessor[ resultColumnProcessors.size() ] )
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.includeInResultRow = ArrayHelper.toBooleanArray( includeInResultRowList );
|
||||||
}
|
}
|
||||||
|
|
||||||
private Queryable determineAppropriateOwnerPersister(NonScalarReturn ownerDescriptor) {
|
private Queryable determineAppropriateOwnerPersister(NonScalarReturn ownerDescriptor) {
|
||||||
|
@ -336,10 +345,18 @@ public class CustomLoader extends Loader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String[] getResultRowAliases() {
|
||||||
|
return transformerAliases;
|
||||||
|
}
|
||||||
|
|
||||||
protected ResultTransformer resolveResultTransformer(ResultTransformer resultTransformer) {
|
protected ResultTransformer resolveResultTransformer(ResultTransformer resultTransformer) {
|
||||||
return HolderInstantiator.resolveResultTransformer( null, resultTransformer );
|
return HolderInstantiator.resolveResultTransformer( null, resultTransformer );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean[] includeInResultRow() {
|
||||||
|
return includeInResultRow;
|
||||||
|
}
|
||||||
|
|
||||||
protected Object getResultColumnOrRow(
|
protected Object getResultColumnOrRow(
|
||||||
Object[] row,
|
Object[] row,
|
||||||
ResultTransformer transformer,
|
ResultTransformer transformer,
|
||||||
|
@ -348,6 +365,11 @@ public class CustomLoader extends Loader {
|
||||||
return rowProcessor.buildResultRow( row, rs, transformer != null, session );
|
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 {
|
protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {
|
||||||
// meant to handle dynamic instantiation queries...(Copy from QueryLoader)
|
// meant to handle dynamic instantiation queries...(Copy from QueryLoader)
|
||||||
HolderInstantiator holderInstantiator = HolderInstantiator.getHolderInstantiator(
|
HolderInstantiator holderInstantiator = HolderInstantiator.getHolderInstantiator(
|
||||||
|
@ -438,6 +460,17 @@ public class CustomLoader extends Loader {
|
||||||
ResultSet resultSet,
|
ResultSet resultSet,
|
||||||
boolean hasTransformer,
|
boolean hasTransformer,
|
||||||
SessionImplementor session) throws SQLException, HibernateException {
|
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;
|
Object[] resultRow;
|
||||||
if ( !hasScalars ) {
|
if ( !hasScalars ) {
|
||||||
resultRow = data;
|
resultRow = data;
|
||||||
|
@ -452,11 +485,7 @@ public class CustomLoader extends Loader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ( hasTransformer )
|
return resultRow;
|
||||||
? resultRow
|
|
||||||
: ( resultRow.length == 1 )
|
|
||||||
? resultRow[0]
|
|
||||||
: resultRow;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,8 @@ package org.hibernate.loader.hql;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -39,7 +39,6 @@ import org.hibernate.QueryException;
|
||||||
import org.hibernate.ScrollableResults;
|
import org.hibernate.ScrollableResults;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.engine.EntityEntry;
|
|
||||||
import org.hibernate.engine.QueryParameters;
|
import org.hibernate.engine.QueryParameters;
|
||||||
import org.hibernate.engine.SessionFactoryImplementor;
|
import org.hibernate.engine.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.SessionImplementor;
|
import org.hibernate.engine.SessionImplementor;
|
||||||
|
@ -382,36 +381,49 @@ public class QueryLoader extends BasicLoader {
|
||||||
return implicitResultTransformer != null;
|
return implicitResultTransformer != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String[] getResultRowAliases() {
|
||||||
|
return queryReturnAliases;
|
||||||
|
}
|
||||||
|
|
||||||
protected ResultTransformer resolveResultTransformer(ResultTransformer resultTransformer) {
|
protected ResultTransformer resolveResultTransformer(ResultTransformer resultTransformer) {
|
||||||
return HolderInstantiator.resolveResultTransformer( implicitResultTransformer, 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)
|
protected Object getResultColumnOrRow(Object[] row, ResultTransformer transformer, ResultSet rs, SessionImplementor session)
|
||||||
throws SQLException, HibernateException {
|
throws SQLException, HibernateException {
|
||||||
|
|
||||||
row = toResultRow( row );
|
Object[] resultRow = getResultRow( row, rs, session );
|
||||||
boolean hasTransform = hasSelectNew() || transformer!=null;
|
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 ) {
|
if ( hasScalars ) {
|
||||||
String[][] scalarColumns = scalarColumnNames;
|
String[][] scalarColumns = scalarColumnNames;
|
||||||
int queryCols = queryReturnTypes.length;
|
int queryCols = queryReturnTypes.length;
|
||||||
if ( !hasTransform && queryCols == 1 ) {
|
resultRow = new Object[queryCols];
|
||||||
return queryReturnTypes[0].nullSafeGet( rs, scalarColumns[0], session, null );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
row = new Object[queryCols];
|
|
||||||
for ( int i = 0; i < queryCols; i++ ) {
|
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 {
|
else {
|
||||||
return row;
|
resultRow = toResultRow( row );
|
||||||
}
|
}
|
||||||
|
return resultRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {
|
protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {
|
||||||
|
|
|
@ -24,9 +24,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.transform;
|
package org.hibernate.transform;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.property.ChainedPropertyAccessor;
|
import org.hibernate.property.ChainedPropertyAccessor;
|
||||||
|
@ -54,7 +52,7 @@ import org.hibernate.property.Setter;
|
||||||
*
|
*
|
||||||
* @author max
|
* @author max
|
||||||
*/
|
*/
|
||||||
public class AliasToBeanResultTransformer implements ResultTransformer, Serializable {
|
public class AliasToBeanResultTransformer extends AliasedTupleSubsetResultTransformer {
|
||||||
|
|
||||||
// IMPL NOTE : due to the delayed population of setters (setters cached
|
// IMPL NOTE : due to the delayed population of setters (setters cached
|
||||||
// for performance), we really cannot properly define equality for
|
// for performance), we really cannot properly define equality for
|
||||||
|
@ -73,6 +71,13 @@ public class AliasToBeanResultTransformer implements ResultTransformer, Serializ
|
||||||
this.resultClass = resultClass;
|
this.resultClass = resultClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public Object transformTuple(Object[] tuple, String[] aliases) {
|
public Object transformTuple(Object[] tuple, String[] aliases) {
|
||||||
Object result;
|
Object result;
|
||||||
|
|
||||||
|
@ -129,10 +134,6 @@ public class AliasToBeanResultTransformer implements ResultTransformer, Serializ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List transformList(List collection) {
|
|
||||||
return collection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if ( this == o ) {
|
if ( this == o ) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -26,7 +26,6 @@ package org.hibernate.transform;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link ResultTransformer} implementation which builds a map for each "row",
|
* {@link ResultTransformer} implementation which builds a map for each "row",
|
||||||
|
@ -38,7 +37,7 @@ import java.io.Serializable;
|
||||||
* @author Gavin King
|
* @author Gavin King
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class AliasToEntityMapResultTransformer extends BasicTransformerAdapter implements Serializable {
|
public class AliasToEntityMapResultTransformer extends AliasedTupleSubsetResultTransformer {
|
||||||
|
|
||||||
public static final AliasToEntityMapResultTransformer INSTANCE = new AliasToEntityMapResultTransformer();
|
public static final AliasToEntityMapResultTransformer INSTANCE = new AliasToEntityMapResultTransformer();
|
||||||
|
|
||||||
|
@ -62,6 +61,13 @@ public class AliasToEntityMapResultTransformer extends BasicTransformerAdapter i
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialization hook for ensuring singleton uniqueing.
|
* 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
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class DistinctResultTransformer extends BasicTransformerAdapter implements Serializable {
|
public class DistinctResultTransformer extends BasicTransformerAdapter {
|
||||||
|
|
||||||
public static final DistinctResultTransformer INSTANCE = new DistinctResultTransformer();
|
public static final DistinctResultTransformer INSTANCE = new DistinctResultTransformer();
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
package org.hibernate.transform;
|
package org.hibernate.transform;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Much like {@link RootEntityResultTransformer}, but we also distinct
|
* Much like {@link RootEntityResultTransformer}, but we also distinct
|
||||||
|
@ -37,7 +36,7 @@ import java.io.Serializable;
|
||||||
* @author Gavin King
|
* @author Gavin King
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class DistinctRootEntityResultTransformer implements ResultTransformer, Serializable {
|
public class DistinctRootEntityResultTransformer implements TupleSubsetResultTransformer {
|
||||||
|
|
||||||
public static final DistinctRootEntityResultTransformer INSTANCE = new DistinctRootEntityResultTransformer();
|
public static final DistinctRootEntityResultTransformer INSTANCE = new DistinctRootEntityResultTransformer();
|
||||||
|
|
||||||
|
@ -68,6 +67,20 @@ public class DistinctRootEntityResultTransformer implements ResultTransformer, S
|
||||||
return DistinctResultTransformer.INSTANCE.transformList( list );
|
return DistinctResultTransformer.INSTANCE.transformList( list );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public boolean[] includeInTransform(String[] aliases, int tupleLength) {
|
||||||
|
return RootEntityResultTransformer.INSTANCE.includeInTransform( aliases, tupleLength );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
|
||||||
|
return RootEntityResultTransformer.INSTANCE.isTransformedValueATupleElement( null, tupleLength );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialization hook for ensuring singleton uniqueing.
|
* Serialization hook for ensuring singleton uniqueing.
|
||||||
*
|
*
|
||||||
|
|
|
@ -24,14 +24,17 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.transform;
|
package org.hibernate.transform;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.util.ArrayHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ???
|
* ???
|
||||||
*
|
*
|
||||||
* @author max
|
* @author max
|
||||||
*/
|
*/
|
||||||
public class PassThroughResultTransformer extends BasicTransformerAdapter implements Serializable {
|
public class PassThroughResultTransformer extends BasicTransformerAdapter implements TupleSubsetResultTransformer {
|
||||||
|
|
||||||
public static final PassThroughResultTransformer INSTANCE = new PassThroughResultTransformer();
|
public static final PassThroughResultTransformer INSTANCE = new PassThroughResultTransformer();
|
||||||
|
|
||||||
|
@ -48,6 +51,39 @@ public class PassThroughResultTransformer extends BasicTransformerAdapter implem
|
||||||
return tuple.length==1 ? tuple[0] : tuple;
|
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.
|
* Serialization hook for ensuring singleton uniqueing.
|
||||||
*
|
*
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.transform;
|
package org.hibernate.transform;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import org.hibernate.util.ArrayHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link ResultTransformer} implementation which limits the result tuple
|
* {@link ResultTransformer} implementation which limits the result tuple
|
||||||
|
@ -36,7 +36,7 @@ import java.io.Serializable;
|
||||||
* @author Gavin King
|
* @author Gavin King
|
||||||
* @author Steve Ebersole
|
* @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();
|
public static final RootEntityResultTransformer INSTANCE = new RootEntityResultTransformer();
|
||||||
|
|
||||||
|
@ -53,6 +53,29 @@ public final class RootEntityResultTransformer extends BasicTransformerAdapter i
|
||||||
return tuple[ tuple.length-1 ];
|
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.
|
* Serialization hook for ensuring singleton uniqueing.
|
||||||
*
|
*
|
||||||
|
|
|
@ -26,13 +26,12 @@ package org.hibernate.transform;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tranforms each result row from a tuple into a {@link List}, such that what
|
* 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}.
|
* 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();
|
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.io.Serializable;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
import org.hibernate.EntityMode;
|
import org.hibernate.EntityMode;
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
|
import org.hibernate.transform.AliasToBeanConstructorResultTransformer;
|
||||||
import org.hibernate.transform.AliasToBeanResultTransformer;
|
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.ResultTransformer;
|
||||||
|
import org.hibernate.transform.RootEntityResultTransformer;
|
||||||
import org.hibernate.transform.DistinctRootEntityResultTransformer;
|
import org.hibernate.transform.DistinctRootEntityResultTransformer;
|
||||||
import org.hibernate.transform.AliasToEntityMapResultTransformer;
|
import org.hibernate.transform.AliasToEntityMapResultTransformer;
|
||||||
import org.hibernate.transform.PassThroughResultTransformer;
|
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.SerializationHelper;
|
||||||
import org.hibernate.util.ArrayHelper;
|
import org.hibernate.util.ArrayHelper;
|
||||||
|
|
||||||
|
@ -52,6 +60,14 @@ public class QueryKeyTest extends TestCase {
|
||||||
public static class AClass implements Serializable {
|
public static class AClass implements Serializable {
|
||||||
private String propAccessedByField;
|
private String propAccessedByField;
|
||||||
private String propAccessedByMethod;
|
private String propAccessedByMethod;
|
||||||
|
private int propValue;
|
||||||
|
|
||||||
|
public AClass() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public AClass(String propAccessedByField) {
|
||||||
|
this.propAccessedByField = propAccessedByField;
|
||||||
|
}
|
||||||
|
|
||||||
public String getPropAccessedByMethod() {
|
public String getPropAccessedByMethod() {
|
||||||
return propAccessedByMethod;
|
return propAccessedByMethod;
|
||||||
|
@ -61,31 +77,156 @@ public class QueryKeyTest extends TestCase {
|
||||||
this.propAccessedByMethod = propAccessedByMethod;
|
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(),
|
// settings are lazily initialized when calling transformTuple(),
|
||||||
// so they have not been initialized for the following test
|
// so they have not been initialized for the following test
|
||||||
// (it *should* be initialized before creating a QueryKey)
|
// (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
|
// initialize settings for the next test
|
||||||
AliasToBeanResultTransformer transformer = new AliasToBeanResultTransformer( AClass.class );
|
AliasToBeanResultTransformer transformer = new AliasToBeanResultTransformer( AClass.class );
|
||||||
transformer.transformTuple(
|
transformer.transformTuple(
|
||||||
new Object[] { "abc", "def" },
|
new Object[] { "abc", "def" },
|
||||||
new String[] { "propAccessedByField", "propAccessedByMethod" } );
|
new String[] { "propAccessedByField", "propAccessedByMethod" }
|
||||||
doTest( buildBasicKey( transformer ) );
|
);
|
||||||
|
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(
|
return new QueryKey(
|
||||||
QUERY_STRING,
|
QUERY_STRING,
|
||||||
ArrayHelper.EMPTY_TYPE_ARRAY, // positional param types
|
ArrayHelper.EMPTY_TYPE_ARRAY, // positional param types
|
||||||
|
@ -100,18 +241,34 @@ public class QueryKeyTest extends TestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doTest(QueryKey key) {
|
private void doTest(QueryKey key) {
|
||||||
HashMap map = new HashMap();
|
Map keyMap = new HashMap();
|
||||||
|
Map transformerMap = new HashMap();
|
||||||
|
|
||||||
map.put( key, "" );
|
keyMap.put( key, "" );
|
||||||
assert map.size() == 1 : "really messed up";
|
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" );
|
if ( key.getResultTransformer() != null ) {
|
||||||
assert old != null && map.size() == 1 : "apparent QueryKey equals/hashCode issue";
|
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
|
// finally, lets serialize it and see what happens
|
||||||
QueryKey key2 = ( QueryKey ) SerializationHelper.clone( key );
|
QueryKey key2 = ( QueryKey ) SerializationHelper.clone( key );
|
||||||
assert key != key2 : "deep copy issue";
|
assert key != key2 : "deep copy issue";
|
||||||
old = map.put( key2, "new value" );
|
old = keyMap.put( key2, "new value" );
|
||||||
assert old != null && map.size() == 1 : "deserialization did not set hashCode or equals properly";
|
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();
|
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() {
|
public void testIllegalMixedTransformerQueries() {
|
||||||
Session session = openSession();
|
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)
|
protected void runTest(HqlExecutor hqlExecutor, CriteriaExecutor criteriaExecutor, ResultChecker checker, boolean isSingleResult)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
createData();
|
createData();
|
||||||
|
try {
|
||||||
if ( criteriaExecutor != null ) {
|
if ( criteriaExecutor != null ) {
|
||||||
runTest( criteriaExecutor, checker, isSingleResult );
|
runTest( criteriaExecutor, checker, isSingleResult );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
deleteData();
|
deleteData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -49,133 +49,4 @@ public class CriteriaQueryCachePutResultTransformerTest extends CriteriaQueryCac
|
||||||
protected boolean areDynamicNonLazyAssociationsChecked() {
|
protected boolean areDynamicNonLazyAssociationsChecked() {
|
||||||
return false;
|
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 {
|
public void testAliasToEntityMapNoProjectionList() throws Exception {
|
||||||
reportSkip( "known to fail using HQL", "HQL query using Transformers.ALIAS_TO_ENTITY_MAP with no projection" );
|
reportSkip( "known to fail using HQL", "HQL query using Transformers.ALIAS_TO_ENTITY_MAP with no projection" );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAliasToEntityMapNoProjectionListFailureExpected() throws Exception {
|
public void testAliasToEntityMapNoProjectionListFailureExpected() throws Exception {
|
||||||
super.testAliasToEntityMapNoProjectionList();
|
super.testAliasToEntityMapNoProjectionList();
|
||||||
}
|
}
|
||||||
|
@ -66,8 +65,23 @@ public class HqlQueryCacheIgnoreResultTransformerTest extends AbstractQueryCache
|
||||||
public void testAliasToEntityMapNoProjectionMultiAndNullList() throws Exception {
|
public void testAliasToEntityMapNoProjectionMultiAndNullList() throws Exception {
|
||||||
reportSkip( "known to fail using HQL", "HQL query using Transformers.ALIAS_TO_ENTITY_MAP with no projection" );
|
reportSkip( "known to fail using HQL", "HQL query using Transformers.ALIAS_TO_ENTITY_MAP with no projection" );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testAliasToEntityMapNoProjectionMultiAndNullListFailureExpected() throws Exception {
|
public void testAliasToEntityMapNoProjectionMultiAndNullListFailureExpected() throws Exception {
|
||||||
super.testAliasToEntityMapNoProjectionMultiAndNullList();
|
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