@@ -247,7 +247,15 @@ private static List<SqmSelection<?>> selections(SqmSelectStatement<?> sqm) {
247
247
char .class , Character .class
248
248
);
249
249
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
+ */
251
259
protected static <T > RowTransformer <T > determineRowTransformer (
252
260
SqmSelectStatement <?> sqm ,
253
261
Class <T > resultClass ,
@@ -260,73 +268,90 @@ else if ( resultClass == null || resultClass == Object.class ) {
260
268
return RowTransformerStandardImpl .instance ();
261
269
}
262
270
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 );
266
272
if ( selections == null ) {
267
- throw new AssertionFailure ("No selections" );
273
+ throw new AssertionFailure ( "No selections" );
268
274
}
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 );
328
350
}
329
351
}
352
+ else {
353
+ return new RowTransformerCheckingImpl <>( resultType );
354
+ }
330
355
}
331
356
332
357
private static <T > RowTransformer <T > makeRowTransformerTupleTransformerAdapter (
0 commit comments