From c922a10df298e094efc5fff26be7e7009751a427 Mon Sep 17 00:00:00 2001 From: Gavin Date: Sat, 6 May 2023 22:22:58 +0200 Subject: [PATCH] HHH-16564 allow null in 'select new' argument list --- .../sqm/sql/BaseSqmToSqlAstConverter.java | 16 ++++++++-- .../sqm/tree/expression/Compatibility.java | 5 ++++ .../hql/CastNullSelectExpressionTest.java | 30 +++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index a8e681f4f8..f9dadc9a3e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -391,10 +391,13 @@ import org.hibernate.type.BasicType; import org.hibernate.type.JavaObjectType; import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; +import org.hibernate.type.descriptor.java.AbstractClassJavaType; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.JavaTypeHelper; import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators; +import org.hibernate.type.descriptor.jdbc.NullJdbcType; import org.hibernate.type.internal.BasicTypeImpl; import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.usertype.UserVersionType; @@ -4872,8 +4875,17 @@ public Expression visitLiteral(SqmLiteral literal) { } final MappingModelExpressible keyExpressible = getKeyExpressible( mappingModelExpressible ); if ( keyExpressible == null ) { - // Default to the Object type - return new QueryLiteral<>( null, basicType( Object.class ) ); + // treat Void as the bottom type, the class of null + return new QueryLiteral<>( null, new JavaObjectType(NullJdbcType.INSTANCE, new AbstractClassJavaType<>(Void.class) { + @Override + public X unwrap(Object value, Class type, WrapperOptions options) { + return null; + } + @Override + public Void wrap(X value, WrapperOptions options) { + return null; + } + } ) ); } final List expressions = new ArrayList<>( keyExpressible.getJdbcTypeCount() ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/Compatibility.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/Compatibility.java index 6e74a5953c..ddc3f908b5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/Compatibility.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/Compatibility.java @@ -57,6 +57,11 @@ public static boolean areAssignmentCompatible(Class to, Class from) { assert to != null; assert from != null; + if ( from == Void.class ) { + // treat Void as the bottom type, the class of null + return true; + } + if ( to.isAssignableFrom( from ) ) { return true; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/CastNullSelectExpressionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/CastNullSelectExpressionTest.java index ea6d90fdeb..d78a695076 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/CastNullSelectExpressionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/CastNullSelectExpressionTest.java @@ -61,6 +61,36 @@ public void testSelectNewCastNull(SessionFactoryScope scope) { ); } + @Test + @TestForIssue(jiraKey = "HHH-16564") + public void testSelectNewNull(SessionFactoryScope scope) { + scope.inTransaction( + (session) -> { + Person result = (Person) session.createQuery( + "select new Person( id, firstName, null, lastName ) from Person where lastName='Munster'" + ).uniqueResult(); + assertEquals( "Herman", result.firstName ); + assertNull( result.middleName ); + assertEquals( "Munster", result.lastName ); + } + ); + } + + @Test + @TestForIssue(jiraKey = "HHH-16564") + public void testSelectNull(SessionFactoryScope scope) { + scope.inTransaction( + (session) -> { + Object[] result = (Object[]) session.createQuery( + "select id, firstName, null, lastName from Person where lastName='Munster'" + ).uniqueResult(); + assertEquals( "Herman", result[1] ); + assertNull( result[2] ); + assertEquals( "Munster", result[3] ); + } + ); + } + @BeforeEach public void createTestData(SessionFactoryScope scope) { scope.inTransaction(