|
5 | 5 | package org.hibernate.query.sql.internal; |
6 | 6 |
|
7 | 7 | import java.io.Serializable; |
| 8 | +import java.lang.reflect.Constructor; |
8 | 9 | import java.time.Instant; |
9 | 10 | import java.util.Calendar; |
10 | 11 | import java.util.Collection; |
@@ -330,25 +331,51 @@ private void handleExplicitResultSetMapping() { |
330 | 331 | setTupleTransformerForResultType( resultType ); |
331 | 332 | } |
332 | 333 | else { |
333 | | - checkResultType( resultType ); |
| 334 | + checkResultType( resultType, resultSetMapping ); |
334 | 335 | } |
335 | 336 | } |
336 | 337 | } |
337 | 338 |
|
338 | | - private void checkResultType(Class<R> resultType) { |
339 | | - switch ( resultSetMapping.getNumberOfResultBuilders() ) { |
340 | | - case 0: |
341 | | - throw new IllegalArgumentException( "Named query exists, but did not specify a resultClass" ); |
342 | | - case 1: |
343 | | - final Class<?> actualResultJavaType = |
344 | | - resultSetMapping.getResultBuilders().get( 0 ).getJavaType(); |
345 | | - if ( actualResultJavaType != null && !resultType.isAssignableFrom( actualResultJavaType ) ) { |
346 | | - throw buildIncompatibleException( resultType, actualResultJavaType ); |
347 | | - } |
348 | | - break; |
349 | | - default: |
350 | | - throw new IllegalArgumentException( "Cannot create TypedQuery for query with more than one return" ); |
| 339 | + private void checkResultType(Class<R> resultType, ResultSetMapping resultSetMapping) { |
| 340 | + // resultType can be null if any of the deprecated methods were used to create the query |
| 341 | + if ( resultType != null && !isResultTypeAlwaysAllowed( resultType )) { |
| 342 | + switch ( resultSetMapping.getNumberOfResultBuilders() ) { |
| 343 | + case 0: |
| 344 | + if ( !resultSetMapping.isDynamic() ) { |
| 345 | + throw new IllegalArgumentException( "Named query exists, but did not specify a resultClass" ); |
| 346 | + } |
| 347 | + break; |
| 348 | + case 1: |
| 349 | + final Class<?> actualResultJavaType = |
| 350 | + resultSetMapping.getResultBuilders().get( 0 ).getJavaType(); |
| 351 | + if ( actualResultJavaType != null && !resultType.isAssignableFrom( actualResultJavaType ) ) { |
| 352 | + throw buildIncompatibleException( resultType, actualResultJavaType ); |
| 353 | + } |
| 354 | + break; |
| 355 | + default: |
| 356 | + // The return type has to be a class with an appropriate constructor, i.e. one whose parameter types match |
| 357 | + // the types of the result builders. If none such constructor is found, throw an IAE |
| 358 | + if ( !validConstructorFoundForResultType( resultType, resultSetMapping ) ) { |
| 359 | + throw new IllegalArgumentException( "The declared return type for a multi-valued result set mapping should be Object[], Map, List, or Tuple" ); |
| 360 | + } |
| 361 | + } |
| 362 | + } |
| 363 | + } |
| 364 | + |
| 365 | + private boolean validConstructorFoundForResultType(Class<R> resultType, ResultSetMapping resultSetMapping) { |
| 366 | + // Only 1 constructor with the right number of parameters is allowed (see NativeQueryConstructorTransformer) |
| 367 | + Constructor<?> constructor = resultType.getConstructors()[0]; |
| 368 | + if ( constructor.getParameterCount() != resultSetMapping.getNumberOfResultBuilders() ) { |
| 369 | + return false; |
| 370 | + } |
| 371 | + final List<ResultBuilder> resultBuilders = resultSetMapping.getResultBuilders(); |
| 372 | + Class<?>[] paramTypes = constructor.getParameterTypes(); |
| 373 | + for ( int i = 0; i < resultBuilders.size(); i++ ) { |
| 374 | + if ( resultBuilders.get( i ).getJavaType() != paramTypes[i] ) { |
| 375 | + return false; |
| 376 | + } |
351 | 377 | } |
| 378 | + return true; |
352 | 379 | } |
353 | 380 |
|
354 | 381 | protected <T> void setTupleTransformerForResultType(Class<T> resultClass) { |
@@ -740,6 +767,7 @@ else if ( !isResultTypeAlwaysAllowed( resultType ) |
740 | 767 | else { |
741 | 768 | mapping = resultSetMapping; |
742 | 769 | } |
| 770 | + checkResultType( resultType, mapping ); |
743 | 771 | return isCacheableQuery() |
744 | 772 | ? getInterpretationCache() |
745 | 773 | .resolveSelectQueryPlan( selectInterpretationsKey( mapping ), () -> createQueryPlan( mapping ) ) |
|
0 commit comments