HHH-18664 Consistent constructor matching logic for row-transformer

This commit is contained in:
Marco Belladelli 2024-09-26 11:58:34 +02:00
parent fc38d88930
commit c5db0d38e7
4 changed files with 48 additions and 59 deletions

View File

@ -287,7 +287,11 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
}
else if ( isClass( resultType ) ) {
try {
return new RowTransformerConstructorImpl<>( resultType, tupleMetadata );
return new RowTransformerConstructorImpl<>(
resultType,
tupleMetadata,
sqm.nodeBuilder().getTypeConfiguration()
);
}
catch (InstantiationException ie) {
return new RowTransformerCheckingImpl<>( resultType );
@ -310,7 +314,11 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
return (RowTransformer<T>) new RowTransformerMapImpl( tupleMetadata );
}
else if ( isClass( resultType ) ) {
return new RowTransformerConstructorImpl<>( resultType, tupleMetadata );
return new RowTransformerConstructorImpl<>(
resultType,
tupleMetadata,
sqm.nodeBuilder().getTypeConfiguration()
);
}
else {
throw new QueryTypeMismatchException( "Result type '" + resultType.getSimpleName()

View File

@ -19,12 +19,12 @@ import org.hibernate.sql.results.graph.DomainResultAssembler;
import org.hibernate.sql.results.graph.InitializerParent;
import org.hibernate.sql.results.graph.instantiation.DynamicInstantiationResult;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.spi.TypeConfiguration;
import org.jboss.logging.Logger;
import static java.util.stream.Collectors.toList;
import static org.hibernate.sql.results.graph.instantiation.internal.InstantiationHelper.isConstructorCompatible;
import static org.hibernate.sql.results.graph.instantiation.internal.InstantiationHelper.findMatchingConstructor;
/**
* @author Steve Ebersole
@ -164,13 +164,14 @@ public class DynamicInstantiationResultImpl<R> implements DynamicInstantiationRe
.getMappingMetamodel()
.getTypeConfiguration();
// find a constructor matching argument types
for ( Constructor<?> constructor : javaType.getJavaTypeClass().getDeclaredConstructors() ) {
if ( isConstructorCompatible( constructor, argumentTypes, typeConfiguration ) ) {
constructor.setAccessible( true );
@SuppressWarnings("unchecked")
final Constructor<R> construct = (Constructor<R>) constructor;
return new DynamicInstantiationAssemblerConstructorImpl<>( construct, javaType, argumentReaders );
}
final Constructor<R> constructor = findMatchingConstructor(
javaType.getJavaTypeClass(),
argumentTypes,
typeConfiguration
);
if ( constructor != null ) {
constructor.setAccessible( true );
return new DynamicInstantiationAssemblerConstructorImpl<>( constructor, javaType, argumentReaders );
}
if ( log.isDebugEnabled() ) {

View File

@ -55,12 +55,20 @@ public class InstantiationHelper {
}
public static boolean isConstructorCompatible(Class<?> javaClass, List<Class<?>> argTypes, TypeConfiguration typeConfiguration) {
for ( Constructor<?> constructor : javaClass.getDeclaredConstructors() ) {
if ( isConstructorCompatible( constructor, argTypes, typeConfiguration) ) {
return true;
return findMatchingConstructor( javaClass, argTypes, typeConfiguration ) != null;
}
public static <T> Constructor<T> findMatchingConstructor(
Class<T> type,
List<Class<?>> argumentTypes,
TypeConfiguration typeConfiguration) {
for ( final Constructor<?> constructor : type.getDeclaredConstructors() ) {
if ( isConstructorCompatible( constructor, argumentTypes, typeConfiguration ) ) {
//noinspection unchecked
return (Constructor<T>) constructor;
}
}
return false;
return null;
}
public static boolean isConstructorCompatible(

View File

@ -13,8 +13,10 @@ import java.util.List;
import org.hibernate.query.sqm.SqmExpressible;
import org.hibernate.query.sqm.tree.SqmExpressibleAccessor;
import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.query.sqm.tree.expression.Compatibility.areAssignmentCompatible;
import static java.util.stream.Collectors.toList;
import static org.hibernate.sql.results.graph.instantiation.internal.InstantiationHelper.findMatchingConstructor;
/**
* {@link RowTransformer} instantiating an arbitrary class
@ -25,25 +27,25 @@ public class RowTransformerConstructorImpl<T> implements RowTransformer<T> {
private final Class<T> type;
private final Constructor<T> constructor;
public RowTransformerConstructorImpl(Class<T> type, TupleMetadata tupleMetadata) {
public RowTransformerConstructorImpl(
Class<T> type,
TupleMetadata tupleMetadata,
TypeConfiguration typeConfiguration) {
this.type = type;
final List<TupleElement<?>> elements = tupleMetadata.getList();
final Class<?>[] sig = new Class[elements.size()];
for (int i = 0; i < elements.size(); i++) {
sig[i] = resolveElementJavaType( elements.get( i ) );
}
if ( sig.length == 1 && sig[0] == null ) {
final List<Class<?>> argumentTypes = elements.stream()
.map( RowTransformerConstructorImpl::resolveElementJavaType )
.collect( toList() );
if ( argumentTypes.size() == 1 && argumentTypes.get( 0 ) == null ) {
// Can not (properly) resolve constructor for single null element
throw new InstantiationException( "Cannot instantiate query result type, argument types are unknown ", type );
}
try {
constructor = findMatchingConstructor( type, sig );
constructor.setAccessible( true );
}
catch (Exception e) {
//TODO try again with primitive types
throw new InstantiationException( "Cannot instantiate query result type ", type, e );
constructor = findMatchingConstructor( type, argumentTypes, typeConfiguration );
if ( constructor == null ) {
throw new InstantiationException( "Cannot instantiate query result type, found no matching constructor", type );
}
constructor.setAccessible( true );
}
private static Class<?> resolveElementJavaType(TupleElement<?> element) {
@ -57,36 +59,6 @@ public class RowTransformerConstructorImpl<T> implements RowTransformer<T> {
return element.getJavaType();
}
private Constructor<T> findMatchingConstructor(Class<T> type, Class<?>[] sig) throws Exception {
try {
return type.getDeclaredConstructor( sig );
}
catch (NoSuchMethodException | SecurityException e) {
constructor_loop:
for ( final Constructor<?> constructor : type.getDeclaredConstructors() ) {
final Class<?>[] parameterTypes = constructor.getParameterTypes();
if ( parameterTypes.length == sig.length ) {
for ( int i = 0; i < sig.length; i++ ) {
final Class<?> parameterType = parameterTypes[i];
final Class<?> argType = sig[i];
final boolean assignmentCompatible;
assignmentCompatible =
argType == null && !parameterType.isPrimitive()
|| areAssignmentCompatible(
parameterType,
argType
);
if ( !assignmentCompatible ) {
continue constructor_loop;
}
}
return (Constructor<T>) constructor;
}
}
throw e;
}
}
@Override
public T transformRow(Object[] row) {
try {