|
5 | 5 | package org.hibernate.query.sql.internal; |
6 | 6 |
|
7 | 7 | import java.io.Serializable; |
8 | | -import java.lang.reflect.Constructor; |
9 | 8 | import java.time.Instant; |
10 | 9 | import java.util.Calendar; |
11 | 10 | import java.util.Collection; |
@@ -355,32 +354,43 @@ private void checkResultType(Class<R> resultType, ResultSetMapping resultSetMapp |
355 | 354 | } |
356 | 355 | break; |
357 | 356 | 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 |
360 | 360 | 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 | + ); |
362 | 365 | } |
363 | 366 | } |
364 | 367 | } |
365 | 368 | } |
366 | 369 |
|
367 | 370 | 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; |
381 | 383 | } |
382 | 384 | } |
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; |
384 | 394 | } |
385 | 395 |
|
386 | 396 | protected <T> void setTupleTransformerForResultType(Class<T> resultClass) { |
|
0 commit comments