HHH-17598 Allow array typed queries without result wrapping

This commit is contained in:
Marco Belladelli 2024-01-10 10:45:56 +01:00 committed by Christian Beikov
parent 2f60e08c64
commit f8d84f9f7f
2 changed files with 53 additions and 36 deletions

View File

@ -195,18 +195,18 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
return new MySqmJdbcExecutionContextAdapter( executionContext, jdbcSelect, subSelectFetchKeyHandler, hql ); return new MySqmJdbcExecutionContextAdapter( executionContext, jdbcSelect, subSelectFetchKeyHandler, hql );
} }
private static Class<?> singleSelectionType(SqmSelectStatement<?> sqm) { private static SqmSelection<?> singleSelection(SqmSelectStatement<?> sqm) {
final List<SqmSelection<?>> selections = sqm.getQueryPart() final List<SqmSelection<?>> selections = sqm.getQueryPart()
.getFirstQuerySpec() .getFirstQuerySpec()
.getSelectClause() .getSelectClause()
.getSelections(); .getSelections();
if ( selections.size() == 1 ) { return selections.size() == 1 ? selections.get( 0 ) : null;
final SqmSelection<?> sqmSelection = selections.get( 0 ); }
return sqmSelection.getSelectableNode().isCompoundSelection() ?
null : private static Class<?> selectionType(SqmSelection<?> selection) {
sqmSelection.getNodeJavaType().getJavaTypeClass(); return selection != null && !selection.getSelectableNode().isCompoundSelection() ?
} selection.getNodeJavaType().getJavaTypeClass()
return null; : null;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -221,37 +221,40 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
else if ( resultType == null ) { else if ( resultType == null ) {
return RowTransformerStandardImpl.instance(); return RowTransformerStandardImpl.instance();
} }
else if ( resultType == Object[].class ) {
return (RowTransformer<T>) RowTransformerArrayImpl.instance();
}
else if ( resultType == List.class && resultType != singleSelectionType( sqm ) ) {
return (RowTransformer<T>) RowTransformerListImpl.instance();
}
else { else {
// NOTE : if we get here : final SqmSelection<?> selection = singleSelection( sqm );
// 1) there is no TupleTransformer specified if ( resultType.isArray() && resultType != selectionType( selection ) ) {
// 2) an explicit result-type, other than an array or List, was specified return (RowTransformer<T>) RowTransformerArrayImpl.instance();
}
if ( tupleMetadata == null ) { else if ( resultType == List.class && resultType != selectionType( selection ) ) {
if ( sqm.getQueryPart().getFirstQuerySpec().getSelectClause().getSelections().size() == 1 ) { return (RowTransformer<T>) RowTransformerListImpl.instance();
return RowTransformerSingularReturnImpl.instance();
}
else {
throw new AssertionFailure( "Query defined multiple selections, should have had TupleMetadata" );
}
} }
else { else {
if ( Tuple.class.equals( resultType ) ) { // NOTE : if we get here :
return (RowTransformer<T>) new RowTransformerJpaTupleImpl( tupleMetadata ); // 1) there is no TupleTransformer specified
} // 2) an explicit result-type, other than an array or List, was specified
else if ( Map.class.equals( resultType ) ) {
return (RowTransformer<T>) new RowTransformerMapImpl( tupleMetadata ); if ( tupleMetadata == null ) {
} if ( selection != null ) {
else if ( isClass( resultType ) ) { return RowTransformerSingularReturnImpl.instance();
return new RowTransformerConstructorImpl<>( resultType, tupleMetadata ); }
else {
throw new AssertionFailure( "Query defined multiple selections, should have had TupleMetadata" );
}
} }
else { else {
throw new InstantiationException( "Query result type is not instantiable", resultType ); if ( Tuple.class.equals( resultType ) ) {
return (RowTransformer<T>) new RowTransformerJpaTupleImpl( tupleMetadata );
}
else if ( Map.class.equals( resultType ) ) {
return (RowTransformer<T>) new RowTransformerMapImpl( tupleMetadata );
}
else if ( isClass( resultType ) ) {
return new RowTransformerConstructorImpl<>( resultType, tupleMetadata );
}
else {
throw new InstantiationException( "Query result type is not instantiable", resultType );
}
} }
} }
} }

View File

@ -47,7 +47,7 @@ public class ConvertedListAttributeQueryTest {
@Test @Test
@SuppressWarnings( "rawtypes" ) @SuppressWarnings( "rawtypes" )
public void testHQL(SessionFactoryScope scope) { public void testListHQL(SessionFactoryScope scope) {
scope.inTransaction( session -> { scope.inTransaction( session -> {
final List resultList = session.createQuery( final List resultList = session.createQuery(
"select emp.phoneNumbers from Employee emp where emp.id = :EMP_ID", "select emp.phoneNumbers from Employee emp where emp.id = :EMP_ID",
@ -59,7 +59,7 @@ public class ConvertedListAttributeQueryTest {
@Test @Test
@SuppressWarnings( "rawtypes" ) @SuppressWarnings( "rawtypes" )
public void testCriteria(SessionFactoryScope scope) { public void testListCriteria(SessionFactoryScope scope) {
scope.inTransaction( session -> { scope.inTransaction( session -> {
final CriteriaBuilder cb = session.getCriteriaBuilder(); final CriteriaBuilder cb = session.getCriteriaBuilder();
final CriteriaQuery<List> q = cb.createQuery( List.class ); final CriteriaQuery<List> q = cb.createQuery( List.class );
@ -71,6 +71,20 @@ public class ConvertedListAttributeQueryTest {
} ); } );
} }
@Test
public void testArrayCriteria(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final CriteriaBuilder cb = session.getCriteriaBuilder();
final CriteriaQuery<Integer[]> q = cb.createQuery( Integer[].class );
final Root<Employee> r = q.from( Employee.class );
q.multiselect( r.get( "id" ), r.get( "id" ) );
q.where( cb.equal( r.get( "id" ), 1 ) );
final Object result = session.createQuery( q ).getSingleResult();
assertThat( result ).isInstanceOf( Object[].class );
assertThat( ( (Object[]) result ) ).containsExactly( 1, 1 );
} );
}
@Entity( name = "Employee" ) @Entity( name = "Employee" )
public static class Employee { public static class Employee {
@Id @Id