HHH-8498 - Full ConstructorResult handling
This commit is contained in:
parent
4b2667cc40
commit
a2881b3a35
|
@ -45,6 +45,7 @@ import org.hibernate.annotations.common.reflection.XClass;
|
|||
import org.hibernate.annotations.common.reflection.XProperty;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.internal.util.type.PrimitiveWrapperHelper;
|
||||
|
||||
/**
|
||||
* No idea.
|
||||
|
@ -141,50 +142,7 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
}
|
||||
|
||||
return converterDefinedType.equals( propertyType )
|
||||
|| arePrimitiveWrapperEquivalents( converterDefinedType, propertyType );
|
||||
}
|
||||
|
||||
private boolean arePrimitiveWrapperEquivalents(Class converterDefinedType, Class propertyType) {
|
||||
if ( converterDefinedType.isPrimitive() ) {
|
||||
return getWrapperEquivalent( converterDefinedType ).equals( propertyType );
|
||||
}
|
||||
else if ( propertyType.isPrimitive() ) {
|
||||
return getWrapperEquivalent( propertyType ).equals( converterDefinedType );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Class getWrapperEquivalent(Class primitive) {
|
||||
if ( ! primitive.isPrimitive() ) {
|
||||
throw new AssertionFailure( "Passed type for which to locate wrapper equivalent was not a primitive" );
|
||||
}
|
||||
|
||||
if ( boolean.class.equals( primitive ) ) {
|
||||
return Boolean.class;
|
||||
}
|
||||
else if ( char.class.equals( primitive ) ) {
|
||||
return Character.class;
|
||||
}
|
||||
else if ( byte.class.equals( primitive ) ) {
|
||||
return Byte.class;
|
||||
}
|
||||
else if ( short.class.equals( primitive ) ) {
|
||||
return Short.class;
|
||||
}
|
||||
else if ( int.class.equals( primitive ) ) {
|
||||
return Integer.class;
|
||||
}
|
||||
else if ( long.class.equals( primitive ) ) {
|
||||
return Long.class;
|
||||
}
|
||||
else if ( float.class.equals( primitive ) ) {
|
||||
return Float.class;
|
||||
}
|
||||
else if ( double.class.equals( primitive ) ) {
|
||||
return Double.class;
|
||||
}
|
||||
|
||||
throw new AssertionFailure( "Unexpected primitive type (VOID most likely) passed to getWrapperEquivalent" );
|
||||
|| PrimitiveWrapperHelper.arePrimitiveWrapperEquivalents( converterDefinedType, propertyType );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -211,7 +211,10 @@ public class PrimitiveWrapperHelper {
|
|||
return (PrimitiveWrapperDescriptor<X>) DoubleDescriptor.INSTANCE;
|
||||
}
|
||||
|
||||
// most likely void.class, which we can't really handle here
|
||||
if ( void.class == primitiveClazz ) {
|
||||
throw new IllegalArgumentException( "void, as primitive type, has no wrapper equivalent" );
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException( "Unrecognized primitive type class : " + primitiveClazz.getName() );
|
||||
}
|
||||
|
||||
|
@ -266,4 +269,14 @@ public class PrimitiveWrapperHelper {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean arePrimitiveWrapperEquivalents(Class converterDefinedType, Class propertyType) {
|
||||
if ( converterDefinedType.isPrimitive() ) {
|
||||
return getDescriptorByPrimitiveType( converterDefinedType ).getWrapperClass().equals( propertyType );
|
||||
}
|
||||
else if ( propertyType.isPrimitive() ) {
|
||||
return getDescriptorByPrimitiveType( propertyType ).getWrapperClass().equals( converterDefinedType );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,13 +22,13 @@
|
|||
* Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
package org.hibernate.loader.custom;
|
||||
package org.hibernate.loader.custom;
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.loader.CollectionAliases;
|
||||
import org.hibernate.loader.EntityAliases;
|
||||
|
||||
/**
|
||||
* Spefically a fetch return that refers to a collection association.
|
||||
* Specifically a fetch return that refers to a collection association.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2013, Red Hat Inc. 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 Inc.
|
||||
*
|
||||
* 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.loader.custom;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.internal.util.type.PrimitiveWrapperHelper;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* Represents a {@link javax.persistence.ConstructorResult} within the custom query.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class ConstructorResultColumnProcessor implements ResultColumnProcessor {
|
||||
private final Class targetClass;
|
||||
private final ScalarResultColumnProcessor[] scalarProcessors;
|
||||
|
||||
private Constructor constructor;
|
||||
|
||||
public ConstructorResultColumnProcessor(Class targetClass, ScalarResultColumnProcessor[] scalarProcessors) {
|
||||
this.targetClass = targetClass;
|
||||
this.scalarProcessors = scalarProcessors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performDiscovery(JdbcResultMetadata metadata, List<Type> types, List<String> aliases) throws SQLException {
|
||||
final List<Type> localTypes = new ArrayList<Type>();
|
||||
for ( ScalarResultColumnProcessor scalar : scalarProcessors ) {
|
||||
scalar.performDiscovery( metadata, localTypes, aliases );
|
||||
}
|
||||
|
||||
types.addAll( localTypes );
|
||||
|
||||
constructor = resolveConstructor( targetClass, localTypes );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object extract(Object[] data, ResultSet resultSet, SessionImplementor session)
|
||||
throws SQLException, HibernateException {
|
||||
if ( constructor == null ) {
|
||||
throw new IllegalStateException( "Constructor to call was null" );
|
||||
}
|
||||
|
||||
final Object[] args = new Object[ scalarProcessors.length ];
|
||||
for ( int i = 0; i < scalarProcessors.length; i++ ) {
|
||||
args[i] = scalarProcessors[i].extract( data, resultSet, session );
|
||||
}
|
||||
|
||||
try {
|
||||
return constructor.newInstance( args );
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
throw new HibernateException(
|
||||
String.format( "Unable to call %s constructor", constructor.getDeclaringClass() ),
|
||||
e
|
||||
);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new HibernateException(
|
||||
String.format( "Unable to call %s constructor", constructor.getDeclaringClass() ),
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static Constructor resolveConstructor(Class targetClass, List<Type> types) {
|
||||
for ( Constructor constructor : targetClass.getConstructors() ) {
|
||||
final Class[] argumentTypes = constructor.getParameterTypes();
|
||||
if ( argumentTypes.length != types.size() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean allMatched = true;
|
||||
for ( int i = 0; i < argumentTypes.length; i++ ) {
|
||||
if ( ! areAssignmentCompatible( argumentTypes[i], types.get( i ).getReturnedClass() ) ) {
|
||||
allMatched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !allMatched ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return constructor;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException( "Could not locate appropriate constructor on class : " + targetClass.getName() );
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static boolean areAssignmentCompatible(Class argumentType, Class typeReturnedClass) {
|
||||
return argumentType.isAssignableFrom( typeReturnedClass )
|
||||
|| PrimitiveWrapperHelper.arePrimitiveWrapperEquivalents( argumentType, typeReturnedClass );
|
||||
}
|
||||
}
|
|
@ -23,52 +23,18 @@
|
|||
*/
|
||||
package org.hibernate.loader.custom;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
/**
|
||||
* A return representing a {@link javax.persistence.ConstructorResult}
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class ConstructorReturn implements Return {
|
||||
private final Class targetClass;
|
||||
private final ScalarReturn[] scalars;
|
||||
|
||||
// private final Constructor constructor;
|
||||
|
||||
public ConstructorReturn(Class targetClass, ScalarReturn[] scalars) {
|
||||
this.targetClass = targetClass;
|
||||
this.scalars = scalars;
|
||||
|
||||
// constructor = resolveConstructor( targetClass, scalars );
|
||||
}
|
||||
|
||||
private static Constructor resolveConstructor(Class targetClass, ScalarReturn[] scalars) {
|
||||
for ( Constructor constructor : targetClass.getConstructors() ) {
|
||||
final Class[] argumentTypes = constructor.getParameterTypes();
|
||||
if ( argumentTypes.length != scalars.length ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean allMatched = true;
|
||||
for ( int i = 0; i < argumentTypes.length; i++ ) {
|
||||
if ( areAssignmentCompatible( argumentTypes[i], scalars[i].getType().getReturnedClass() ) ) {
|
||||
allMatched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !allMatched ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return constructor;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException( "Could not locate appropriate constructor on class : " + targetClass.getName() );
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static boolean areAssignmentCompatible(Class argumentType, Class typeReturnedClass) {
|
||||
// todo : add handling for primitive/wrapper equivalents
|
||||
return argumentType.isAssignableFrom( typeReturnedClass );
|
||||
}
|
||||
|
||||
public Class getTargetClass() {
|
||||
|
@ -78,8 +44,4 @@ public class ConstructorReturn implements Return {
|
|||
public ScalarReturn[] getScalars() {
|
||||
return scalars;
|
||||
}
|
||||
|
||||
// public Constructor getConstructor() {
|
||||
// return constructor;
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -24,14 +24,10 @@
|
|||
package org.hibernate.loader.custom;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -107,29 +103,28 @@ public class CustomLoader extends Loader {
|
|||
this.querySpaces.addAll( customQuery.getQuerySpaces() );
|
||||
this.namedParameterBindPoints = customQuery.getNamedParameterBindPoints();
|
||||
|
||||
List entityPersisters = new ArrayList();
|
||||
List entityOwners = new ArrayList();
|
||||
List entityAliases = new ArrayList();
|
||||
List<Queryable> entityPersisters = new ArrayList<Queryable>();
|
||||
List<Integer> entityOwners = new ArrayList<Integer>();
|
||||
List<EntityAliases> entityAliases = new ArrayList<EntityAliases>();
|
||||
|
||||
List collectionPersisters = new ArrayList();
|
||||
List collectionOwners = new ArrayList();
|
||||
List collectionAliases = new ArrayList();
|
||||
List<QueryableCollection> collectionPersisters = new ArrayList<QueryableCollection>();
|
||||
List<Integer> collectionOwners = new ArrayList<Integer>();
|
||||
List<CollectionAliases> collectionAliases = new ArrayList<CollectionAliases>();
|
||||
|
||||
List<LockMode> lockModes = new ArrayList<LockMode>();
|
||||
List<ResultColumnProcessor> resultColumnProcessors = new ArrayList<ResultColumnProcessor>();
|
||||
List<Return> nonScalarReturnList = new ArrayList<Return>();
|
||||
List<Type> resultTypes = new ArrayList<Type>();
|
||||
List<String> specifiedAliases = new ArrayList<String>();
|
||||
|
||||
List lockModes = new ArrayList();
|
||||
List resultColumnProcessors = new ArrayList();
|
||||
List nonScalarReturnList = new ArrayList();
|
||||
List resultTypes = new ArrayList();
|
||||
List specifiedAliases = new ArrayList();
|
||||
int returnableCounter = 0;
|
||||
boolean hasScalars = false;
|
||||
|
||||
List includeInResultRowList = new ArrayList();
|
||||
List<Boolean> includeInResultRowList = new ArrayList<Boolean>();
|
||||
|
||||
Iterator itr = customQuery.getCustomQueryReturns().iterator();
|
||||
while ( itr.hasNext() ) {
|
||||
final Return rtn = ( Return ) itr.next();
|
||||
for ( Return rtn : customQuery.getCustomQueryReturns() ) {
|
||||
if ( rtn instanceof ScalarReturn ) {
|
||||
ScalarReturn scalarRtn = ( ScalarReturn ) rtn;
|
||||
ScalarReturn scalarRtn = (ScalarReturn) rtn;
|
||||
resultTypes.add( scalarRtn.getType() );
|
||||
specifiedAliases.add( scalarRtn.getColumnAlias() );
|
||||
resultColumnProcessors.add(
|
||||
|
@ -147,7 +142,7 @@ public class CustomLoader extends Loader {
|
|||
includeInResultRowList.add( true );
|
||||
hasScalars = true;
|
||||
|
||||
ScalarResultColumnProcessor[] scalarProcessors = new ScalarResultColumnProcessor[ constructorReturn.getScalars().length ];
|
||||
ScalarResultColumnProcessor[] scalarProcessors = new ScalarResultColumnProcessor[constructorReturn.getScalars().length];
|
||||
int i = 0;
|
||||
for ( ScalarReturn scalarReturn : constructorReturn.getScalars() ) {
|
||||
scalarProcessors[i++] = new ScalarResultColumnProcessor(
|
||||
|
@ -161,8 +156,8 @@ public class CustomLoader extends Loader {
|
|||
);
|
||||
}
|
||||
else if ( rtn instanceof RootReturn ) {
|
||||
RootReturn rootRtn = ( RootReturn ) rtn;
|
||||
Queryable persister = ( Queryable ) factory.getEntityPersister( rootRtn.getEntityName() );
|
||||
RootReturn rootRtn = (RootReturn) rtn;
|
||||
Queryable persister = (Queryable) factory.getEntityPersister( rootRtn.getEntityName() );
|
||||
entityPersisters.add( persister );
|
||||
lockModes.add( (rootRtn.getLockMode()) );
|
||||
resultColumnProcessors.add( new NonScalarResultColumnProcessor( returnableCounter++ ) );
|
||||
|
@ -175,9 +170,9 @@ public class CustomLoader extends Loader {
|
|||
includeInResultRowList.add( true );
|
||||
}
|
||||
else if ( rtn instanceof CollectionReturn ) {
|
||||
CollectionReturn collRtn = ( CollectionReturn ) rtn;
|
||||
CollectionReturn collRtn = (CollectionReturn) rtn;
|
||||
String role = collRtn.getOwnerEntityName() + "." + collRtn.getOwnerProperty();
|
||||
QueryableCollection persister = ( QueryableCollection ) factory.getCollectionPersister( role );
|
||||
QueryableCollection persister = (QueryableCollection) factory.getCollectionPersister( role );
|
||||
collectionPersisters.add( persister );
|
||||
lockModes.add( collRtn.getLockMode() );
|
||||
resultColumnProcessors.add( new NonScalarResultColumnProcessor( returnableCounter++ ) );
|
||||
|
@ -189,7 +184,7 @@ public class CustomLoader extends Loader {
|
|||
// determine if the collection elements are entities...
|
||||
Type elementType = persister.getElementType();
|
||||
if ( elementType.isEntityType() ) {
|
||||
Queryable elementPersister = ( Queryable ) ( ( EntityType ) elementType ).getAssociatedJoinable( factory );
|
||||
Queryable elementPersister = (Queryable) ((EntityType) elementType).getAssociatedJoinable( factory );
|
||||
entityPersisters.add( elementPersister );
|
||||
entityOwners.add( -1 );
|
||||
entityAliases.add( collRtn.getElementEntityAliases() );
|
||||
|
@ -198,15 +193,15 @@ public class CustomLoader extends Loader {
|
|||
includeInResultRowList.add( true );
|
||||
}
|
||||
else if ( rtn instanceof EntityFetchReturn ) {
|
||||
EntityFetchReturn fetchRtn = ( EntityFetchReturn ) rtn;
|
||||
EntityFetchReturn fetchRtn = (EntityFetchReturn) rtn;
|
||||
NonScalarReturn ownerDescriptor = fetchRtn.getOwner();
|
||||
int ownerIndex = nonScalarReturnList.indexOf( ownerDescriptor );
|
||||
entityOwners.add( ownerIndex );
|
||||
lockModes.add( fetchRtn.getLockMode() );
|
||||
Queryable ownerPersister = determineAppropriateOwnerPersister( ownerDescriptor );
|
||||
EntityType fetchedType = ( EntityType ) ownerPersister.getPropertyType( fetchRtn.getOwnerProperty() );
|
||||
EntityType fetchedType = (EntityType) ownerPersister.getPropertyType( fetchRtn.getOwnerProperty() );
|
||||
String entityName = fetchedType.getAssociatedEntityName( getFactory() );
|
||||
Queryable persister = ( Queryable ) factory.getEntityPersister( entityName );
|
||||
Queryable persister = (Queryable) factory.getEntityPersister( entityName );
|
||||
entityPersisters.add( persister );
|
||||
nonScalarReturnList.add( rtn );
|
||||
specifiedAliases.add( fetchRtn.getAlias() );
|
||||
|
@ -215,14 +210,14 @@ public class CustomLoader extends Loader {
|
|||
includeInResultRowList.add( false );
|
||||
}
|
||||
else if ( rtn instanceof CollectionFetchReturn ) {
|
||||
CollectionFetchReturn fetchRtn = ( CollectionFetchReturn ) rtn;
|
||||
CollectionFetchReturn fetchRtn = (CollectionFetchReturn) rtn;
|
||||
NonScalarReturn ownerDescriptor = fetchRtn.getOwner();
|
||||
int ownerIndex = nonScalarReturnList.indexOf( ownerDescriptor );
|
||||
collectionOwners.add( ownerIndex );
|
||||
lockModes.add( fetchRtn.getLockMode() );
|
||||
Queryable ownerPersister = determineAppropriateOwnerPersister( ownerDescriptor );
|
||||
String role = ownerPersister.getEntityName() + '.' + fetchRtn.getOwnerProperty();
|
||||
QueryableCollection persister = ( QueryableCollection ) factory.getCollectionPersister( role );
|
||||
QueryableCollection persister = (QueryableCollection) factory.getCollectionPersister( role );
|
||||
collectionPersisters.add( persister );
|
||||
nonScalarReturnList.add( rtn );
|
||||
specifiedAliases.add( fetchRtn.getAlias() );
|
||||
|
@ -230,7 +225,7 @@ public class CustomLoader extends Loader {
|
|||
// determine if the collection elements are entities...
|
||||
Type elementType = persister.getElementType();
|
||||
if ( elementType.isEntityType() ) {
|
||||
Queryable elementPersister = ( Queryable ) ( ( EntityType ) elementType ).getAssociatedJoinable( factory );
|
||||
Queryable elementPersister = (Queryable) ((EntityType) elementType).getAssociatedJoinable( factory );
|
||||
entityPersisters.add( elementPersister );
|
||||
entityOwners.add( ownerIndex );
|
||||
entityAliases.add( fetchRtn.getElementEntityAliases() );
|
||||
|
@ -245,27 +240,27 @@ public class CustomLoader extends Loader {
|
|||
|
||||
this.entityPersisters = new Queryable[ entityPersisters.size() ];
|
||||
for ( int i = 0; i < entityPersisters.size(); i++ ) {
|
||||
this.entityPersisters[i] = ( Queryable ) entityPersisters.get( i );
|
||||
this.entityPersisters[i] = entityPersisters.get( i );
|
||||
}
|
||||
this.entiytOwners = ArrayHelper.toIntArray( entityOwners );
|
||||
this.entityAliases = new EntityAliases[ entityAliases.size() ];
|
||||
for ( int i = 0; i < entityAliases.size(); i++ ) {
|
||||
this.entityAliases[i] = ( EntityAliases ) entityAliases.get( i );
|
||||
this.entityAliases[i] = entityAliases.get( i );
|
||||
}
|
||||
|
||||
this.collectionPersisters = new QueryableCollection[ collectionPersisters.size() ];
|
||||
for ( int i = 0; i < collectionPersisters.size(); i++ ) {
|
||||
this.collectionPersisters[i] = ( QueryableCollection ) collectionPersisters.get( i );
|
||||
this.collectionPersisters[i] = collectionPersisters.get( i );
|
||||
}
|
||||
this.collectionOwners = ArrayHelper.toIntArray( collectionOwners );
|
||||
this.collectionAliases = new CollectionAliases[ collectionAliases.size() ];
|
||||
for ( int i = 0; i < collectionAliases.size(); i++ ) {
|
||||
this.collectionAliases[i] = ( CollectionAliases ) collectionAliases.get( i );
|
||||
this.collectionAliases[i] = collectionAliases.get( i );
|
||||
}
|
||||
|
||||
this.lockModes = new LockMode[ lockModes.size() ];
|
||||
for ( int i = 0; i < lockModes.size(); i++ ) {
|
||||
this.lockModes[i] = ( LockMode ) lockModes.get( i );
|
||||
this.lockModes[i] = lockModes.get( i );
|
||||
}
|
||||
|
||||
this.resultTypes = ArrayHelper.toTypeArray( resultTypes );
|
||||
|
@ -273,7 +268,7 @@ public class CustomLoader extends Loader {
|
|||
|
||||
this.rowProcessor = new ResultRowProcessor(
|
||||
hasScalars,
|
||||
( ResultColumnProcessor[] ) resultColumnProcessors.toArray( new ResultColumnProcessor[ resultColumnProcessors.size() ] )
|
||||
resultColumnProcessors.toArray( new ResultColumnProcessor[ resultColumnProcessors.size() ] )
|
||||
);
|
||||
|
||||
this.includeInResultRow = ArrayHelper.toBooleanArray( includeInResultRowList );
|
||||
|
@ -385,9 +380,8 @@ public class CustomLoader extends Loader {
|
|||
return sql;
|
||||
}
|
||||
|
||||
public ScrollableResults scroll(
|
||||
final QueryParameters queryParameters,
|
||||
final SessionImplementor session) throws HibernateException {
|
||||
public ScrollableResults scroll(final QueryParameters queryParameters, final SessionImplementor session)
|
||||
throws HibernateException {
|
||||
return scroll(
|
||||
queryParameters,
|
||||
resultTypes,
|
||||
|
@ -436,6 +430,7 @@ public class CustomLoader extends Loader {
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected List getResultList(List results, ResultTransformer resultTransformer) throws QueryException {
|
||||
// meant to handle dynamic instantiation queries...(Copy from QueryLoader)
|
||||
HolderInstantiator holderInstantiator = HolderInstantiator.getHolderInstantiator(
|
||||
|
@ -489,233 +484,16 @@ public class CustomLoader extends Loader {
|
|||
}
|
||||
|
||||
|
||||
public class ResultRowProcessor {
|
||||
private final boolean hasScalars;
|
||||
private ResultColumnProcessor[] columnProcessors;
|
||||
|
||||
public ResultRowProcessor(boolean hasScalars, ResultColumnProcessor[] columnProcessors) {
|
||||
this.hasScalars = hasScalars || ( columnProcessors == null || columnProcessors.length == 0 );
|
||||
this.columnProcessors = columnProcessors;
|
||||
}
|
||||
|
||||
public void prepareForAutoDiscovery(Metadata metadata) throws SQLException {
|
||||
if ( columnProcessors == null || columnProcessors.length == 0 ) {
|
||||
int columns = metadata.getColumnCount();
|
||||
columnProcessors = new ResultColumnProcessor[ columns ];
|
||||
for ( int i = 1; i <= columns; i++ ) {
|
||||
columnProcessors[ i - 1 ] = new ScalarResultColumnProcessor( i );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a logical result row.
|
||||
* <p/>
|
||||
* At this point, Loader has already processed all non-scalar result data. We
|
||||
* just need to account for scalar result data here...
|
||||
*
|
||||
* @param data Entity data defined as "root returns" and already handled by the
|
||||
* normal Loader mechanism.
|
||||
* @param resultSet The JDBC result set (positioned at the row currently being processed).
|
||||
* @param hasTransformer Does this query have an associated {@link ResultTransformer}
|
||||
* @param session The session from which the query request originated.
|
||||
* @return The logical result row
|
||||
* @throws SQLException
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public Object buildResultRow(
|
||||
Object[] data,
|
||||
ResultSet resultSet,
|
||||
boolean hasTransformer,
|
||||
SessionImplementor session) throws SQLException, HibernateException {
|
||||
Object[] resultRow = buildResultRow( data, resultSet, session );
|
||||
return ( hasTransformer )
|
||||
? resultRow
|
||||
: ( resultRow.length == 1 )
|
||||
? resultRow[0]
|
||||
: resultRow;
|
||||
}
|
||||
public Object[] buildResultRow(
|
||||
Object[] data,
|
||||
ResultSet resultSet,
|
||||
SessionImplementor session) throws SQLException, HibernateException {
|
||||
Object[] resultRow;
|
||||
if ( !hasScalars ) {
|
||||
resultRow = data;
|
||||
}
|
||||
else {
|
||||
// build an array with indices equal to the total number
|
||||
// of actual returns in the result Hibernate will return
|
||||
// for this query (scalars + non-scalars)
|
||||
resultRow = new Object[ columnProcessors.length ];
|
||||
for ( int i = 0; i < columnProcessors.length; i++ ) {
|
||||
resultRow[i] = columnProcessors[i].extract( data, resultSet, session );
|
||||
}
|
||||
}
|
||||
|
||||
return resultRow;
|
||||
}
|
||||
}
|
||||
|
||||
private static interface ResultColumnProcessor {
|
||||
public Object extract(Object[] data, ResultSet resultSet, SessionImplementor session) throws SQLException, HibernateException;
|
||||
public void performDiscovery(Metadata metadata, List<Type> types, List<String> aliases) throws SQLException, HibernateException;
|
||||
}
|
||||
|
||||
public class NonScalarResultColumnProcessor implements ResultColumnProcessor {
|
||||
private final int position;
|
||||
|
||||
public NonScalarResultColumnProcessor(int position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object extract(
|
||||
Object[] data,
|
||||
ResultSet resultSet,
|
||||
SessionImplementor session) throws SQLException, HibernateException {
|
||||
return data[ position ];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performDiscovery(Metadata metadata, List<Type> types, List<String> aliases) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class ScalarResultColumnProcessor implements ResultColumnProcessor {
|
||||
private int position = -1;
|
||||
private String alias;
|
||||
private Type type;
|
||||
|
||||
public ScalarResultColumnProcessor(int position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public ScalarResultColumnProcessor(String alias, Type type) {
|
||||
this.alias = alias;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object extract(
|
||||
Object[] data,
|
||||
ResultSet resultSet,
|
||||
SessionImplementor session) throws SQLException, HibernateException {
|
||||
return type.nullSafeGet( resultSet, alias, session, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performDiscovery(Metadata metadata, List<Type> types, List<String> aliases) throws SQLException {
|
||||
if ( alias == null ) {
|
||||
alias = metadata.getColumnName( position );
|
||||
}
|
||||
else if ( position < 0 ) {
|
||||
position = metadata.resolveColumnPosition( alias );
|
||||
}
|
||||
if ( type == null ) {
|
||||
type = metadata.getHibernateType( position );
|
||||
}
|
||||
types.add( type );
|
||||
aliases.add( alias );
|
||||
}
|
||||
}
|
||||
|
||||
public class ConstructorResultColumnProcessor implements ResultColumnProcessor {
|
||||
private final Class targetClass;
|
||||
private final ScalarResultColumnProcessor[] scalarProcessors;
|
||||
|
||||
private Constructor constructor;
|
||||
|
||||
public ConstructorResultColumnProcessor(Class targetClass, ScalarResultColumnProcessor[] scalarProcessors) {
|
||||
this.targetClass = targetClass;
|
||||
this.scalarProcessors = scalarProcessors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object extract(Object[] data, ResultSet resultSet, SessionImplementor session)
|
||||
throws SQLException, HibernateException {
|
||||
if ( constructor == null ) {
|
||||
throw new IllegalStateException( "Constructor to call was null" );
|
||||
}
|
||||
|
||||
final Object[] args = new Object[ scalarProcessors.length ];
|
||||
for ( int i = 0; i < scalarProcessors.length; i++ ) {
|
||||
args[i] = scalarProcessors[i].extract( data, resultSet, session );
|
||||
}
|
||||
|
||||
try {
|
||||
return constructor.newInstance( args );
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
throw new HibernateException(
|
||||
String.format( "Unable to call %s constructor", constructor.getDeclaringClass() ),
|
||||
e
|
||||
);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new HibernateException(
|
||||
String.format( "Unable to call %s constructor", constructor.getDeclaringClass() ),
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performDiscovery(Metadata metadata, List<Type> types, List<String> aliases) throws SQLException {
|
||||
final List<Type> localTypes = new ArrayList<Type>();
|
||||
for ( ScalarResultColumnProcessor scalar : scalarProcessors ) {
|
||||
scalar.performDiscovery( metadata, localTypes, aliases );
|
||||
}
|
||||
|
||||
types.addAll( localTypes );
|
||||
|
||||
constructor = resolveConstructor( targetClass, localTypes );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static Constructor resolveConstructor(Class targetClass, List<Type> types) {
|
||||
for ( Constructor constructor : targetClass.getConstructors() ) {
|
||||
final Class[] argumentTypes = constructor.getParameterTypes();
|
||||
if ( argumentTypes.length != types.size() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean allMatched = true;
|
||||
for ( int i = 0; i < argumentTypes.length; i++ ) {
|
||||
if ( ! areAssignmentCompatible( argumentTypes[i], types.get( i ).getReturnedClass() ) ) {
|
||||
allMatched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !allMatched ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return constructor;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException( "Could not locate appropriate constructor on class : " + targetClass.getName() );
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static boolean areAssignmentCompatible(Class argumentType, Class typeReturnedClass) {
|
||||
// todo : add handling for primitive/wrapper equivalents
|
||||
return argumentType.isAssignableFrom( typeReturnedClass );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void autoDiscoverTypes(ResultSet rs) {
|
||||
try {
|
||||
Metadata metadata = new Metadata( getFactory(), rs );
|
||||
JdbcResultMetadata metadata = new JdbcResultMetadata( getFactory(), rs );
|
||||
rowProcessor.prepareForAutoDiscovery( metadata );
|
||||
|
||||
List<String> aliases = new ArrayList<String>();
|
||||
List<Type> types = new ArrayList<Type>();
|
||||
for ( int i = 0; i < rowProcessor.columnProcessors.length; i++ ) {
|
||||
rowProcessor.columnProcessors[i].performDiscovery( metadata, types, aliases );
|
||||
for ( ResultColumnProcessor resultProcessor : rowProcessor.getColumnProcessors() ) {
|
||||
resultProcessor.performDiscovery( metadata, types, aliases );
|
||||
}
|
||||
|
||||
validateAliases( aliases );
|
||||
|
@ -742,75 +520,14 @@ public class CustomLoader extends Loader {
|
|||
boolean alreadyExisted = !aliasesSet.add( alias );
|
||||
if ( alreadyExisted ) {
|
||||
throw new NonUniqueDiscoveredSqlAliasException(
|
||||
"Encountered a duplicated sql alias [" + alias +
|
||||
"] during auto-discovery of a native-sql query"
|
||||
"Encountered a duplicated sql alias [" + alias + "] during auto-discovery of a native-sql query"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
protected void validateAlias(String alias) {
|
||||
}
|
||||
|
||||
private static class Metadata {
|
||||
private final SessionFactoryImplementor factory;
|
||||
private final ResultSet resultSet;
|
||||
private final ResultSetMetaData resultSetMetaData;
|
||||
|
||||
public Metadata(SessionFactoryImplementor factory, ResultSet resultSet) throws HibernateException {
|
||||
try {
|
||||
this.factory = factory;
|
||||
this.resultSet = resultSet;
|
||||
this.resultSetMetaData = resultSet.getMetaData();
|
||||
}
|
||||
catch( SQLException e ) {
|
||||
throw new HibernateException( "Could not extract result set metadata", e );
|
||||
}
|
||||
}
|
||||
|
||||
public int getColumnCount() throws HibernateException {
|
||||
try {
|
||||
return resultSetMetaData.getColumnCount();
|
||||
}
|
||||
catch( SQLException e ) {
|
||||
throw new HibernateException( "Could not determine result set column count", e );
|
||||
}
|
||||
}
|
||||
|
||||
public int resolveColumnPosition(String columnName) throws HibernateException {
|
||||
try {
|
||||
return resultSet.findColumn( columnName );
|
||||
}
|
||||
catch( SQLException e ) {
|
||||
throw new HibernateException( "Could not resolve column name in result set [" + columnName + "]", e );
|
||||
}
|
||||
}
|
||||
|
||||
public String getColumnName(int position) throws HibernateException {
|
||||
try {
|
||||
return factory.getDialect().getColumnAliasExtractor().extractColumnAlias( resultSetMetaData, position );
|
||||
}
|
||||
catch( SQLException e ) {
|
||||
throw new HibernateException( "Could not resolve column name [" + position + "]", e );
|
||||
}
|
||||
}
|
||||
|
||||
public Type getHibernateType(int columnPos) throws SQLException {
|
||||
int columnType = resultSetMetaData.getColumnType( columnPos );
|
||||
int scale = resultSetMetaData.getScale( columnPos );
|
||||
int precision = resultSetMetaData.getPrecision( columnPos );
|
||||
int length = precision;
|
||||
if ( columnType == 1 && precision == 0 ) {
|
||||
length = resultSetMetaData.getColumnDisplaySize( columnPos );
|
||||
}
|
||||
return factory.getTypeResolver().heuristicType(
|
||||
factory.getDialect().getHibernateTypeName(
|
||||
columnType,
|
||||
length,
|
||||
precision,
|
||||
scale
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2013, Red Hat Inc. 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 Inc.
|
||||
*
|
||||
* 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.loader.custom;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* Simplified access to JDBC ResultSetMetaData
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
class JdbcResultMetadata {
|
||||
private final SessionFactoryImplementor factory;
|
||||
private final ResultSet resultSet;
|
||||
private final ResultSetMetaData resultSetMetaData;
|
||||
|
||||
public JdbcResultMetadata(SessionFactoryImplementor factory, ResultSet resultSet) throws HibernateException {
|
||||
try {
|
||||
this.factory = factory;
|
||||
this.resultSet = resultSet;
|
||||
this.resultSetMetaData = resultSet.getMetaData();
|
||||
}
|
||||
catch( SQLException e ) {
|
||||
throw new HibernateException( "Could not extract result set metadata", e );
|
||||
}
|
||||
}
|
||||
|
||||
public int getColumnCount() throws HibernateException {
|
||||
try {
|
||||
return resultSetMetaData.getColumnCount();
|
||||
}
|
||||
catch( SQLException e ) {
|
||||
throw new HibernateException( "Could not determine result set column count", e );
|
||||
}
|
||||
}
|
||||
|
||||
public int resolveColumnPosition(String columnName) throws HibernateException {
|
||||
try {
|
||||
return resultSet.findColumn( columnName );
|
||||
}
|
||||
catch( SQLException e ) {
|
||||
throw new HibernateException( "Could not resolve column name in result set [" + columnName + "]", e );
|
||||
}
|
||||
}
|
||||
|
||||
public String getColumnName(int position) throws HibernateException {
|
||||
try {
|
||||
return factory.getDialect().getColumnAliasExtractor().extractColumnAlias( resultSetMetaData, position );
|
||||
}
|
||||
catch( SQLException e ) {
|
||||
throw new HibernateException( "Could not resolve column name [" + position + "]", e );
|
||||
}
|
||||
}
|
||||
|
||||
public Type getHibernateType(int columnPos) throws SQLException {
|
||||
int columnType = resultSetMetaData.getColumnType( columnPos );
|
||||
int scale = resultSetMetaData.getScale( columnPos );
|
||||
int precision = resultSetMetaData.getPrecision( columnPos );
|
||||
|
||||
int length = precision;
|
||||
if ( columnType == Types.CHAR && precision == 0 ) {
|
||||
length = resultSetMetaData.getColumnDisplaySize( columnPos );
|
||||
}
|
||||
|
||||
return factory.getTypeResolver().heuristicType(
|
||||
factory.getDialect().getHibernateTypeName(
|
||||
columnType,
|
||||
length,
|
||||
precision,
|
||||
scale
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2013, Red Hat Inc. 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 Inc.
|
||||
*
|
||||
* 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.loader.custom;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* Represents non-scalar returns within the custom query. Most of the heavy lifting for non-scalar results
|
||||
* is done within Loader itself.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class NonScalarResultColumnProcessor implements ResultColumnProcessor {
|
||||
private final int position;
|
||||
|
||||
public NonScalarResultColumnProcessor(int position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performDiscovery(JdbcResultMetadata metadata, List<Type> types, List<String> aliases) {
|
||||
// nothing to discover for non-scalar results
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object extract(Object[] data, ResultSet resultSet, SessionImplementor session)
|
||||
throws SQLException, HibernateException {
|
||||
return data[ position ];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2013, Red Hat Inc. 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 Inc.
|
||||
*
|
||||
* 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.loader.custom;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/** Processor for each "column" in a custom query result. May map to more than one physical column in the JDBC ResultSert.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
interface ResultColumnProcessor {
|
||||
/**
|
||||
* Perform discovery, if needed. Typically discovery activities include looking up the column name in the
|
||||
* ResultSet or JDBC type codes.
|
||||
*
|
||||
* @param metadata Delegate for accessing metadata about the JDBC ResultSet
|
||||
* @param types The building List of types
|
||||
* @param aliases The building list of column names/aliases
|
||||
*
|
||||
* @throws SQLException Indicates a problem accessing the JDBC objects
|
||||
* @throws HibernateException Indicates a higher-level problem already categorized by Hibernate
|
||||
*/
|
||||
public void performDiscovery(JdbcResultMetadata metadata, List<Type> types, List<String> aliases)
|
||||
throws SQLException, HibernateException;
|
||||
|
||||
/**
|
||||
* Perform The extraction
|
||||
*
|
||||
* @param data All non-scalar results (handled at a higher level than these processors)
|
||||
* @param resultSet The JDBC result set.
|
||||
* @param session The Hibernate Session
|
||||
*
|
||||
* @return The extracted value
|
||||
*
|
||||
* @throws SQLException Indicates a problem accessing the JDBC objects
|
||||
* @throws HibernateException Indicates a higher-level problem already categorized by Hibernate
|
||||
*/
|
||||
public Object extract(Object[] data, ResultSet resultSet, SessionImplementor session)
|
||||
throws SQLException, HibernateException;
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2013, Red Hat Inc. 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 Inc.
|
||||
*
|
||||
* 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.loader.custom;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
|
||||
/**
|
||||
* Models an entire "row" of results within a custom query
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class ResultRowProcessor {
|
||||
private final boolean hasScalars;
|
||||
private ResultColumnProcessor[] columnProcessors;
|
||||
|
||||
public ResultRowProcessor(boolean hasScalars, ResultColumnProcessor[] columnProcessors) {
|
||||
this.hasScalars = hasScalars || ( columnProcessors == null || columnProcessors.length == 0 );
|
||||
this.columnProcessors = columnProcessors;
|
||||
}
|
||||
|
||||
public ResultColumnProcessor[] getColumnProcessors() {
|
||||
return columnProcessors;
|
||||
}
|
||||
|
||||
public void prepareForAutoDiscovery(JdbcResultMetadata metadata) throws SQLException {
|
||||
if ( columnProcessors == null || columnProcessors.length == 0 ) {
|
||||
int columns = metadata.getColumnCount();
|
||||
columnProcessors = new ResultColumnProcessor[ columns ];
|
||||
for ( int i = 1; i <= columns; i++ ) {
|
||||
columnProcessors[ i - 1 ] = new ScalarResultColumnProcessor( i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a logical result row.
|
||||
* <p/>
|
||||
* At this point, Loader has already processed all non-scalar result data. We
|
||||
* just need to account for scalar result data here...
|
||||
*
|
||||
* @param data Entity data defined as "root returns" and already handled by the
|
||||
* normal Loader mechanism.
|
||||
* @param resultSet The JDBC result set (positioned at the row currently being processed).
|
||||
* @param hasTransformer Does this query have an associated {@link org.hibernate.transform.ResultTransformer}
|
||||
* @param session The session from which the query request originated.
|
||||
* @return The logical result row
|
||||
* @throws java.sql.SQLException
|
||||
* @throws org.hibernate.HibernateException
|
||||
*/
|
||||
public Object buildResultRow(Object[] data, ResultSet resultSet, boolean hasTransformer, SessionImplementor session)
|
||||
throws SQLException, HibernateException {
|
||||
final Object[] resultRow = buildResultRow( data, resultSet, session );
|
||||
if ( hasTransformer ) {
|
||||
return resultRow;
|
||||
}
|
||||
else {
|
||||
return resultRow.length == 1
|
||||
? resultRow[0]
|
||||
: resultRow;
|
||||
}
|
||||
}
|
||||
|
||||
public Object[] buildResultRow(Object[] data, ResultSet resultSet, SessionImplementor session)
|
||||
throws SQLException, HibernateException {
|
||||
Object[] resultRow;
|
||||
if ( !hasScalars ) {
|
||||
resultRow = data;
|
||||
}
|
||||
else {
|
||||
// build an array with indices equal to the total number
|
||||
// of actual returns in the result Hibernate will return
|
||||
// for this query (scalars + non-scalars)
|
||||
resultRow = new Object[ columnProcessors.length ];
|
||||
for ( int i = 0; i < columnProcessors.length; i++ ) {
|
||||
resultRow[i] = columnProcessors[i].extract( data, resultSet, session );
|
||||
}
|
||||
}
|
||||
|
||||
return resultRow;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2013, Red Hat Inc. 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 Inc.
|
||||
*
|
||||
* 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.loader.custom;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* Represents a scalar result within the custom query
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class ScalarResultColumnProcessor implements ResultColumnProcessor {
|
||||
private int position = -1;
|
||||
private String alias;
|
||||
private Type type;
|
||||
|
||||
public ScalarResultColumnProcessor(int position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public ScalarResultColumnProcessor(String alias, Type type) {
|
||||
this.alias = alias;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performDiscovery(JdbcResultMetadata metadata, List<Type> types, List<String> aliases) throws SQLException {
|
||||
if ( alias == null ) {
|
||||
alias = metadata.getColumnName( position );
|
||||
}
|
||||
else if ( position < 0 ) {
|
||||
position = metadata.resolveColumnPosition( alias );
|
||||
}
|
||||
if ( type == null ) {
|
||||
type = metadata.getHibernateType( position );
|
||||
}
|
||||
types.add( type );
|
||||
aliases.add( alias );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object extract(Object[] data, ResultSet resultSet, SessionImplementor session)
|
||||
throws SQLException, HibernateException {
|
||||
return type.nullSafeGet( resultSet, alias, session, null );
|
||||
}
|
||||
}
|
|
@ -29,8 +29,10 @@ import javax.persistence.ConstructorResult;
|
|||
import javax.persistence.Entity;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.NamedNativeQueries;
|
||||
import javax.persistence.NamedNativeQuery;
|
||||
import javax.persistence.SqlResultSetMapping;
|
||||
import javax.persistence.SqlResultSetMappings;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
@ -52,22 +54,54 @@ import static org.junit.Assert.assertEquals;
|
|||
*/
|
||||
public class ConstructorResultNativeQueryTest extends BaseEntityManagerFunctionalTestCase {
|
||||
@Entity( name = "Person" )
|
||||
@SqlResultSetMapping(
|
||||
name = "person-id-and-name",
|
||||
classes = {
|
||||
@ConstructorResult(
|
||||
targetClass = Person.class,
|
||||
columns = {
|
||||
@ColumnResult( name = "id" ),
|
||||
@ColumnResult( name = "p_name" )
|
||||
@SqlResultSetMappings(
|
||||
value = {
|
||||
@SqlResultSetMapping(
|
||||
name = "person-id-and-name",
|
||||
classes = {
|
||||
@ConstructorResult(
|
||||
targetClass = Person.class,
|
||||
columns = {
|
||||
@ColumnResult( name = "id" ),
|
||||
@ColumnResult( name = "p_name" )
|
||||
}
|
||||
)
|
||||
}
|
||||
),
|
||||
@SqlResultSetMapping(
|
||||
name = "person-id-and-name2",
|
||||
classes = {
|
||||
@ConstructorResult(
|
||||
targetClass = Person.class,
|
||||
columns = {
|
||||
@ColumnResult( name = "id" ),
|
||||
@ColumnResult( name = "p_name" )
|
||||
}
|
||||
),
|
||||
@ConstructorResult(
|
||||
targetClass = Person.class,
|
||||
columns = {
|
||||
@ColumnResult( name = "id2" ),
|
||||
@ColumnResult( name = "p_name2" )
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
@NamedNativeQuery(
|
||||
name = "person-id-and-name",
|
||||
query = "select p.id, p.p_name from person p order by p.p_name",
|
||||
resultSetMapping = "person-id-and-name"
|
||||
@NamedNativeQueries(
|
||||
value = {
|
||||
@NamedNativeQuery(
|
||||
name = "person-id-and-name",
|
||||
query = "select p.id, p.p_name from person p order by p.p_name",
|
||||
resultSetMapping = "person-id-and-name"
|
||||
),
|
||||
@NamedNativeQuery(
|
||||
name = "person-id-and-name2",
|
||||
query = "select p.id, p.p_name, p.id as id2, p.p_name as p_name2 from person p order by p.p_name",
|
||||
resultSetMapping = "person-id-and-name2"
|
||||
)
|
||||
}
|
||||
)
|
||||
public static class Person {
|
||||
@Id
|
||||
|
@ -123,4 +157,31 @@ public class ConstructorResultNativeQueryTest extends BaseEntityManagerFunctiona
|
|||
em.getTransaction().commit();
|
||||
em.close();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testMultipleConstructorResultNativeQuery() {
|
||||
EntityManager em = getOrCreateEntityManager();
|
||||
em.getTransaction().begin();
|
||||
em.persist( new Person( 1, "John", new Date() ) );
|
||||
em.getTransaction().commit();
|
||||
em.close();
|
||||
|
||||
em = getOrCreateEntityManager();
|
||||
em.getTransaction().begin();
|
||||
List results = em.createNamedQuery( "person-id-and-name2" ).getResultList();
|
||||
assertEquals( 1, results.size() );
|
||||
Object[] result = assertTyping( Object[].class, results.get( 0 ) );
|
||||
assertEquals( 2, result.length );
|
||||
assertTyping( Person.class, result[0] );
|
||||
assertTyping( Person.class, result[1] );
|
||||
em.getTransaction().commit();
|
||||
em.close();
|
||||
|
||||
em = getOrCreateEntityManager();
|
||||
em.getTransaction().begin();
|
||||
em.createQuery( "delete from Person" ).executeUpdate();
|
||||
em.getTransaction().commit();
|
||||
em.close();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue