Skip to content

Commit 3b3ee64

Browse files
committed
HHH-19383 partial fix to checking of constructor signature
NativeQueryConstructorTransformer does not prevent the result type from having multiple constructors, it just rejects a result type with multiple constructors with matching arity
1 parent fe7a4d7 commit 3b3ee64

File tree

1 file changed

+28
-18
lines changed

1 file changed

+28
-18
lines changed

hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
package org.hibernate.query.sql.internal;
66

77
import java.io.Serializable;
8-
import java.lang.reflect.Constructor;
98
import java.time.Instant;
109
import java.util.Calendar;
1110
import java.util.Collection;
@@ -355,32 +354,43 @@ private void checkResultType(Class<R> resultType, ResultSetMapping resultSetMapp
355354
}
356355
break;
357356
default:
358-
// The return type has to be a class with an appropriate constructor, i.e. one whose parameter types match
359-
// the types of the result builders. If none such constructor is found, throw an IAE
357+
// The return type has to be a class with an appropriate constructor,
358+
// i.e. one whose parameter types match the types of the result builders.
359+
// If no such constructor is found, throw an IAE
360360
if ( !validConstructorFoundForResultType( resultType, resultSetMapping ) ) {
361-
throw new IllegalArgumentException( "The declared return type for a multi-valued result set mapping should be Object[], Map, List, or Tuple" );
361+
throw new IllegalArgumentException(
362+
"The return type for a multivalued result set mapping should be Object[], Map, List, or Tuple"
363+
+ " or it must have an appropriate constructor"
364+
);
362365
}
363366
}
364367
}
365368
}
366369

367370
private boolean validConstructorFoundForResultType(Class<R> resultType, ResultSetMapping resultSetMapping) {
368-
// Only 1 constructor with the right number of parameters is allowed (see NativeQueryConstructorTransformer)
369-
Constructor<?> constructor = resultType.getConstructors()[0];
370-
if ( constructor.getParameterCount() != resultSetMapping.getNumberOfResultBuilders() ) {
371-
return false;
372-
}
373-
final List<ResultBuilder> resultBuilders = resultSetMapping.getResultBuilders();
374-
Class<?>[] paramTypes = constructor.getParameterTypes();
375-
for ( int i = 0; i < resultBuilders.size(); i++ ) {
376-
if (
377-
resultBuilders.get( i ).getJavaType() != ( paramTypes[i].isPrimitive() ?
378-
getDescriptorByPrimitiveType(paramTypes[i] ).getWrapperClass() :
379-
paramTypes[i]) ) {
380-
return false;
371+
// TODO: Only one constructor with the right number of parameters is allowed
372+
// (see NativeQueryConstructorTransformer) so we should validate that
373+
outer: for ( var constructor : resultType.getConstructors() ) {
374+
if ( constructor.getParameterCount() == resultSetMapping.getNumberOfResultBuilders() ) {
375+
final var resultBuilders = resultSetMapping.getResultBuilders();
376+
final var paramTypes = constructor.getParameterTypes();
377+
for ( int i = 0; i < resultBuilders.size(); i++ ) {
378+
if ( !constructorParameterMatches( resultBuilders.get( i ), paramTypes[i] ) ) {
379+
continue outer;
380+
}
381+
}
382+
return true;
381383
}
382384
}
383-
return true;
385+
return false;
386+
}
387+
388+
private static boolean constructorParameterMatches(ResultBuilder resultBuilder, Class<?> paramType) {
389+
final Class<?> parameterClass =
390+
paramType.isPrimitive()
391+
? getDescriptorByPrimitiveType( paramType ).getWrapperClass()
392+
: paramType;
393+
return resultBuilder.getJavaType() == parameterClass;
384394
}
385395

386396
protected <T> void setTupleTransformerForResultType(Class<T> resultClass) {

0 commit comments

Comments
 (0)