Skip to content

Commit 65ed2bd

Browse files
committed
add some comments and extract some methods in implicit instantiation stuff
1 parent 02e053c commit 65ed2bd

File tree

3 files changed

+108
-65
lines changed

3 files changed

+108
-65
lines changed

hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,15 @@ protected <T> void setTupleTransformerForResultType(Class<T> resultClass) {
356356
}
357357
}
358358

359+
/**
360+
* If the result type of the query is {@link Tuple}, {@link Map}, {@link List},
361+
* or any record or class type with an appropriate constructor which is NOT a
362+
* registered basic type, then we attempt to repackage the result tuple as an
363+
* instance of the result type using an appropriate {@link TupleTransformer}.
364+
*
365+
* @param resultClass The requested result type of the query
366+
* @return A {@link TupleTransformer} responsible for repackaging the result type
367+
*/
359368
protected @Nullable TupleTransformer<?> determineTupleTransformerForResultType(Class<?> resultClass) {
360369
if ( Tuple.class.equals( resultClass ) ) {
361370
return NativeQueryTupleTransformer.INSTANCE;
@@ -369,7 +378,8 @@ else if ( List.class.equals( resultClass ) ) {
369378
else if ( resultClass != Object.class && resultClass != Object[].class ) {
370379
// TODO: this is extremely fragile and probably a bug
371380
if ( isClass( resultClass ) && !hasJavaTypeDescriptor( resultClass ) ) {
372-
// not a basic type
381+
// not a basic type, so something we can attempt
382+
// to instantiate to repackage the results
373383
return new NativeQueryConstructorTransformer<>( resultClass );
374384
}
375385
}

hibernate-core/src/main/java/org/hibernate/query/sqm/internal/ConcreteSqmSelectQueryPlan.java

Lines changed: 89 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -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(

hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmUtil.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,6 +1222,14 @@ private static <T> void verifySelectionType(
12221222
}
12231223
}
12241224

1225+
/**
1226+
* Any query result can be represented as a {@link Tuple}, {@link List}, or {@link Map},
1227+
* simply by repackaging the result tuple. Also, any query result is assignable to
1228+
* {@code Object}, or can be returned as an instance of {@code Object[]}.
1229+
*
1230+
* @see ConcreteSqmSelectQueryPlan#determineRowTransformer
1231+
* @see org.hibernate.query.sql.internal.NativeQueryImpl#determineTupleTransformerForResultType
1232+
*/
12251233
public static boolean isResultTypeAlwaysAllowed(Class<?> expectedResultClass) {
12261234
return expectedResultClass == null
12271235
|| expectedResultClass == Object.class

0 commit comments

Comments
 (0)