|
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;
|
|
126 | 127 | import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty;
|
127 | 128 | import static org.hibernate.internal.util.collections.CollectionHelper.isNotEmpty;
|
128 | 129 | import static org.hibernate.internal.util.collections.CollectionHelper.makeCopy;
|
| 130 | +import static org.hibernate.internal.util.type.PrimitiveWrapperHelper.getDescriptorByPrimitiveType; |
129 | 131 | import static org.hibernate.jpa.HibernateHints.HINT_NATIVE_LOCK_MODE;
|
130 | 132 | import static org.hibernate.query.results.internal.Builders.resultClassBuilder;
|
131 | 133 | import static org.hibernate.query.results.ResultSetMapping.resolveResultSetMapping;
|
@@ -331,25 +333,54 @@ private void handleExplicitResultSetMapping() {
|
331 | 333 | setTupleTransformerForResultType( resultType );
|
332 | 334 | }
|
333 | 335 | else {
|
334 |
| - checkResultType( resultType ); |
| 336 | + checkResultType( resultType, resultSetMapping ); |
335 | 337 | }
|
336 | 338 | }
|
337 | 339 | }
|
338 | 340 |
|
339 |
| - private void checkResultType(Class<R> resultType) { |
340 |
| - switch ( resultSetMapping.getNumberOfResultBuilders() ) { |
341 |
| - case 0: |
342 |
| - throw new IllegalArgumentException( "Named query exists, but did not specify a resultClass" ); |
343 |
| - case 1: |
344 |
| - final Class<?> actualResultJavaType = |
345 |
| - resultSetMapping.getResultBuilders().get( 0 ).getJavaType(); |
346 |
| - if ( actualResultJavaType != null && !resultType.isAssignableFrom( actualResultJavaType ) ) { |
347 |
| - throw buildIncompatibleException( resultType, actualResultJavaType ); |
348 |
| - } |
349 |
| - break; |
350 |
| - default: |
351 |
| - throw new IllegalArgumentException( "Cannot create TypedQuery for query with more than one return" ); |
| 341 | + private void checkResultType(Class<R> resultType, ResultSetMapping resultSetMapping) { |
| 342 | + // resultType can be null if any of the deprecated methods were used to create the query |
| 343 | + if ( resultType != null && !isResultTypeAlwaysAllowed( resultType )) { |
| 344 | + switch ( resultSetMapping.getNumberOfResultBuilders() ) { |
| 345 | + case 0: |
| 346 | + if ( !resultSetMapping.isDynamic() ) { |
| 347 | + throw new IllegalArgumentException( "Named query exists, but did not specify a resultClass" ); |
| 348 | + } |
| 349 | + break; |
| 350 | + case 1: |
| 351 | + final Class<?> actualResultJavaType = |
| 352 | + resultSetMapping.getResultBuilders().get( 0 ).getJavaType(); |
| 353 | + if ( actualResultJavaType != null && !resultType.isAssignableFrom( actualResultJavaType ) ) { |
| 354 | + throw buildIncompatibleException( resultType, actualResultJavaType ); |
| 355 | + } |
| 356 | + break; |
| 357 | + 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 |
| 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" ); |
| 362 | + } |
| 363 | + } |
| 364 | + } |
| 365 | + } |
| 366 | + |
| 367 | + 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; |
| 381 | + } |
352 | 382 | }
|
| 383 | + return true; |
353 | 384 | }
|
354 | 385 |
|
355 | 386 | protected <T> void setTupleTransformerForResultType(Class<T> resultClass) {
|
@@ -747,6 +778,7 @@ else if ( !isResultTypeAlwaysAllowed( resultType )
|
747 | 778 | else {
|
748 | 779 | mapping = resultSetMapping;
|
749 | 780 | }
|
| 781 | + checkResultType( resultType, mapping ); |
750 | 782 | return isCacheableQuery()
|
751 | 783 | ? getInterpretationCache()
|
752 | 784 | .resolveSelectQueryPlan( selectInterpretationsKey( mapping ), () -> createQueryPlan( mapping ) )
|
|
0 commit comments