@@ -247,7 +247,15 @@ private static List<SqmSelection<?>> selections(SqmSelectStatement<?> sqm) {
247247 char .class , Character .class
248248 );
249249
250- @ SuppressWarnings ("unchecked" )
250+ /**
251+ * If the result type of the query is {@link Tuple}, {@link Map}, {@link List},
252+ * or any record or class type with an appropriate constructor, then we attempt
253+ * to repackage the result tuple as an instance of the result type using an
254+ * appropriate {@link RowTransformer}.
255+ *
256+ * @param resultClass The requested result type of the query
257+ * @return A {@link RowTransformer} responsible for repackaging the result type
258+ */
251259 protected static <T > RowTransformer <T > determineRowTransformer (
252260 SqmSelectStatement <?> sqm ,
253261 Class <T > resultClass ,
@@ -260,73 +268,90 @@ else if ( resultClass == null || resultClass == Object.class ) {
260268 return RowTransformerStandardImpl .instance ();
261269 }
262270 else {
263- final Class <T > resultType = (Class <T >)
264- WRAPPERS .getOrDefault ( resultClass , resultClass );
265- final List <SqmSelection <?>> selections = selections ( sqm );
271+ final var selections = selections ( sqm );
266272 if ( selections == null ) {
267- throw new AssertionFailure ("No selections" );
273+ throw new AssertionFailure ( "No selections" );
268274 }
269- switch ( selections .size () ) {
270- case 0 :
271- throw new AssertionFailure ("No selections" );
272- case 1 :
273- final SqmSelection <?> selection = selections .get (0 );
274- if ( isSelectionAssignableToResultType ( selection , resultType ) ) {
275- return RowTransformerSingularReturnImpl .instance ();
276- }
277- else if ( resultType .isArray () ) {
278- return (RowTransformer <T >) RowTransformerArrayImpl .instance ();
279- }
280- else if ( List .class .equals ( resultType ) ) {
281- return (RowTransformer <T >) RowTransformerListImpl .instance ();
282- }
283- else if ( Tuple .class .equals ( resultType ) ) {
284- return (RowTransformer <T >) new RowTransformerJpaTupleImpl ( tupleMetadata );
285- }
286- else if ( Map .class .equals ( resultType ) ) {
287- return (RowTransformer <T >) new RowTransformerMapImpl ( tupleMetadata );
288- }
289- else if ( isClass ( resultType ) ) {
290- try {
291- return new RowTransformerConstructorImpl <>(
292- resultType ,
293- tupleMetadata ,
294- sqm .nodeBuilder ().getTypeConfiguration ()
295- );
296- }
297- catch (InstantiationException ie ) {
298- return new RowTransformerCheckingImpl <>( resultType );
299- }
300- }
301- else {
302- return new RowTransformerCheckingImpl <>( resultType );
303- }
304- default :
305- if ( resultType .isArray () ) {
306- return (RowTransformer <T >) RowTransformerArrayImpl .instance ();
307- }
308- else if ( List .class .equals ( resultType ) ) {
309- return (RowTransformer <T >) RowTransformerListImpl .instance ();
310- }
311- else if ( Tuple .class .equals ( resultType ) ) {
312- return (RowTransformer <T >) new RowTransformerJpaTupleImpl ( tupleMetadata );
313- }
314- else if ( Map .class .equals ( resultType ) ) {
315- return (RowTransformer <T >) new RowTransformerMapImpl ( tupleMetadata );
316- }
317- else if ( isClass ( resultType ) ) {
318- return new RowTransformerConstructorImpl <>(
319- resultType ,
320- tupleMetadata ,
321- sqm .nodeBuilder ().getTypeConfiguration ()
322- );
323- }
324- else {
325- throw new QueryTypeMismatchException ( "Result type '" + resultType .getSimpleName ()
326- + "' cannot be used to package the selected expressions" );
327- }
275+ else {
276+ final Class <T > resultType = primitiveToWrapper ( resultClass );
277+ return switch ( selections .size () ) {
278+ case 0 -> throw new AssertionFailure ( "No selections" );
279+ case 1 -> singleItemRowTransformer ( sqm , tupleMetadata , selections .get ( 0 ), resultType );
280+ default -> multipleItemRowTransformer ( sqm , tupleMetadata , resultType );
281+ };
282+ }
283+ }
284+ }
285+
286+ /**
287+ * We tolerate the use of primitive query result types, for example,
288+ * {@code long.class} instead of {@code Long.class}. Note that this
289+ * has no semantics: we don't attempt to enforce that the query
290+ * result is non-null if it is primitive.
291+ */
292+ @ SuppressWarnings ("unchecked" )
293+ private static <T > Class <T > primitiveToWrapper (Class <T > resultClass ) {
294+ // this cast, which looks like complete nonsense, is perfectly correct,
295+ // since Java assigns the type Class<Long> to te expression long.class
296+ // even though the resulting class object is distinct from Long.class
297+ return (Class <T >) WRAPPERS .getOrDefault ( resultClass , resultClass );
298+ }
299+
300+ @ SuppressWarnings ("unchecked" )
301+ private static <T > RowTransformer <T > multipleItemRowTransformer
302+ (SqmSelectStatement <?> sqm , TupleMetadata tupleMetadata , Class <T > resultType ) {
303+ if ( resultType .isArray () ) {
304+ return (RowTransformer <T >) RowTransformerArrayImpl .instance ();
305+ }
306+ else if ( List .class .equals ( resultType ) ) {
307+ return (RowTransformer <T >) RowTransformerListImpl .instance ();
308+ }
309+ else if ( Tuple .class .equals ( resultType ) ) {
310+ return (RowTransformer <T >) new RowTransformerJpaTupleImpl ( tupleMetadata );
311+ }
312+ else if ( Map .class .equals ( resultType ) ) {
313+ return (RowTransformer <T >) new RowTransformerMapImpl ( tupleMetadata );
314+ }
315+ else if ( isClass ( resultType ) ) {
316+ return new RowTransformerConstructorImpl <>( resultType , tupleMetadata ,
317+ sqm .nodeBuilder ().getTypeConfiguration () );
318+ }
319+ else {
320+ throw new QueryTypeMismatchException ( "Result type '" + resultType .getSimpleName ()
321+ + "' cannot be used to package the selected expressions" );
322+ }
323+ }
324+
325+ @ SuppressWarnings ("unchecked" )
326+ private static <T > RowTransformer <T > singleItemRowTransformer
327+ (SqmSelectStatement <?> sqm , TupleMetadata tupleMetadata , SqmSelection <?> selection , Class <T > resultType ) {
328+ if ( isSelectionAssignableToResultType ( selection , resultType ) ) {
329+ return RowTransformerSingularReturnImpl .instance ();
330+ }
331+ else if ( resultType .isArray () ) {
332+ return (RowTransformer <T >) RowTransformerArrayImpl .instance ();
333+ }
334+ else if ( List .class .equals ( resultType ) ) {
335+ return (RowTransformer <T >) RowTransformerListImpl .instance ();
336+ }
337+ else if ( Tuple .class .equals ( resultType ) ) {
338+ return (RowTransformer <T >) new RowTransformerJpaTupleImpl ( tupleMetadata );
339+ }
340+ else if ( Map .class .equals ( resultType ) ) {
341+ return (RowTransformer <T >) new RowTransformerMapImpl ( tupleMetadata );
342+ }
343+ else if ( isClass ( resultType ) ) {
344+ try {
345+ return new RowTransformerConstructorImpl <>( resultType , tupleMetadata ,
346+ sqm .nodeBuilder ().getTypeConfiguration () );
347+ }
348+ catch (InstantiationException ie ) {
349+ return new RowTransformerCheckingImpl <>( resultType );
328350 }
329351 }
352+ else {
353+ return new RowTransformerCheckingImpl <>( resultType );
354+ }
330355 }
331356
332357 private static <T > RowTransformer <T > makeRowTransformerTupleTransformerAdapter (
0 commit comments