From 55efac69b535bf8ab7c7b1707c1c722e9b84f62a Mon Sep 17 00:00:00 2001 From: Nathan Xu Date: Tue, 10 May 2022 22:57:51 -0400 Subject: [PATCH 1/2] HHH-15283 - fix NPE for NamedNativeQuery + SqlResultSetMapping (columns) --- ...pleteResultBuilderBasicValuedStandard.java | 6 +-- .../query/sql/internal/NativeQueryImpl.java | 2 +- .../NamedNativeQueryWithCountColumnTest.java | 54 +++++++++++++++++++ 3 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/NamedNativeQueryWithCountColumnTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderBasicValuedStandard.java b/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderBasicValuedStandard.java index 3dc5a58363aa..05de47888c9c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderBasicValuedStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderBasicValuedStandard.java @@ -27,7 +27,7 @@ /** * ResultBuilder for scalar results defined via: * * @author Steve Ebersole @@ -60,7 +60,7 @@ public ResultBuilder cacheKeyInstance() { @Override public Class getJavaType() { - return explicitJavaType.getJavaTypeClass(); + return explicitJavaType == null ? null : explicitJavaType.getJavaTypeClass(); } @Override @@ -173,7 +173,7 @@ public boolean equals(Object o) { public int hashCode() { int result = explicitColumnName != null ? explicitColumnName.hashCode() : 0; result = 31 * result + ( explicitType != null ? explicitType.hashCode() : 0 ); - result = 31 * result + explicitJavaType.hashCode(); + result = 31 * result + ( explicitJavaType != null ? explicitJavaType.hashCode() : 0 ); return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java index 3ec4fbadc85d..f2964773024c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java @@ -278,7 +278,7 @@ else if ( resultJavaType != null && resultJavaType != Object[].class ) { throw new IllegalArgumentException( "Named query exists but its result type is not compatible" ); case 1: final Class actualResultJavaType = resultSetMapping.getResultBuilders().get( 0 ).getJavaType(); - if ( !resultJavaType.isAssignableFrom( actualResultJavaType ) ) { + if ( actualResultJavaType != null && !resultJavaType.isAssignableFrom( actualResultJavaType ) ) { throw buildIncompatibleException( resultJavaType, actualResultJavaType ); } break; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/NamedNativeQueryWithCountColumnTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/NamedNativeQueryWithCountColumnTest.java new file mode 100644 index 000000000000..bd3bb7006b36 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/NamedNativeQueryWithCountColumnTest.java @@ -0,0 +1,54 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.query.resultmapping; + +import org.hibernate.cfg.AvailableSettings; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.Jpa; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.ColumnResult; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.NamedNativeQuery; +import jakarta.persistence.SqlResultSetMapping; + +/** + * @author Nathan Xu + */ +@Jpa( + annotatedClasses = NamedNativeQueryWithCountColumnTest.Sample.class, + properties = @Setting(name = AvailableSettings.GLOBALLY_QUOTED_IDENTIFIERS, value = "true") +) +@TestForIssue(jiraKey = "HHH-15070") +class NamedNativeQueryWithCountColumnTest { + + @Test + void testNoNullPointerExceptionThrown(EntityManagerFactoryScope scope) { + scope.inTransaction( em ->em.createNamedQuery( "sample.count", Long.class ) ); + } + + @SqlResultSetMapping( + name = "mapping", + columns = @ColumnResult( name = "cnt" ) + ) + @NamedNativeQuery( + name = "sample.count", + resultSetMapping = "mapping", + query = "SELECT count(*) AS cnt FROM Sample" + ) + @Entity(name = "Sample") + static class Sample { + + @Id + Long id; + + } +} From 0a1aa962283709b6c9987c47371bd24f2fe3bcc9 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 19 May 2022 12:04:01 +0200 Subject: [PATCH 2/2] HHH-15283 - fix NPE for NamedNativeQuery + SqlResultSetMapping (columns) --- ...pleteResultBuilderBasicValuedStandard.java | 17 ++--- .../NamedNativeQueryWithCountColumnTest.java | 67 ++++++++++++++++--- 2 files changed, 63 insertions(+), 21 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderBasicValuedStandard.java b/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderBasicValuedStandard.java index 05de47888c9c..ffd03f3596a1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderBasicValuedStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderBasicValuedStandard.java @@ -157,23 +157,14 @@ public boolean equals(Object o) { if ( o == null || getClass() != o.getClass() ) { return false; } - CompleteResultBuilderBasicValuedStandard that = (CompleteResultBuilderBasicValuedStandard) o; - - if ( !Objects.equals( explicitColumnName, that.explicitColumnName ) ) { - return false; - } - if ( !Objects.equals( explicitType, that.explicitType ) ) { - return false; - } - return explicitJavaType.equals( that.explicitJavaType ); + return Objects.equals( explicitColumnName, that.explicitColumnName ) + && Objects.equals( explicitType, that.explicitType ) + && Objects.equals( explicitJavaType, that.explicitJavaType ); } @Override public int hashCode() { - int result = explicitColumnName != null ? explicitColumnName.hashCode() : 0; - result = 31 * result + ( explicitType != null ? explicitType.hashCode() : 0 ); - result = 31 * result + ( explicitJavaType != null ? explicitJavaType.hashCode() : 0 ); - return result; + return Objects.hash( explicitColumnName, explicitType, explicitJavaType ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/NamedNativeQueryWithCountColumnTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/NamedNativeQueryWithCountColumnTest.java index bd3bb7006b36..4e50bf90bf58 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/NamedNativeQueryWithCountColumnTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/resultmapping/NamedNativeQueryWithCountColumnTest.java @@ -7,11 +7,15 @@ package org.hibernate.orm.test.query.resultmapping; import org.hibernate.cfg.AvailableSettings; +import org.hibernate.dialect.H2Dialect; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; import org.hibernate.testing.orm.junit.Jpa; +import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import jakarta.persistence.ColumnResult; @@ -19,6 +23,10 @@ import jakarta.persistence.Id; import jakarta.persistence.NamedNativeQuery; import jakarta.persistence.SqlResultSetMapping; +import jakarta.persistence.Table; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; /** * @author Nathan Xu @@ -30,25 +38,68 @@ @TestForIssue(jiraKey = "HHH-15070") class NamedNativeQueryWithCountColumnTest { + @BeforeEach + public void setUp(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + for ( int i = 0; i < 3; i++ ) { + Sample sample = new Sample( i, String.valueOf( i ) ); + entityManager.persist( sample ); + } + } + ); + } + + @AfterEach + public void tearDown(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> + entityManager.createQuery( "delete from Sample" ).executeUpdate() + ); + } + @Test - void testNoNullPointerExceptionThrown(EntityManagerFactoryScope scope) { - scope.inTransaction( em ->em.createNamedQuery( "sample.count", Long.class ) ); + void testNamedNativeQuery(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> + entityManager.createNamedQuery( "sample.count", Long.class ) + ); + } + + @Test + @RequiresDialect(H2Dialect.class) + void testNamedNativeQueryExecution(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + Long count = entityManager.createNamedQuery( "sample.count", Long.class ).getSingleResult(); + assertThat( count, is( 3L ) ); + } ); } @SqlResultSetMapping( - name = "mapping", - columns = @ColumnResult( name = "cnt" ) + name = "mapping", + columns = @ColumnResult(name = "cnt") ) @NamedNativeQuery( - name = "sample.count", - resultSetMapping = "mapping", - query = "SELECT count(*) AS cnt FROM Sample" + name = "sample.count", + resultSetMapping = "mapping", + query = "SELECT count(*) AS cnt FROM SAMPLE_TABLE" ) @Entity(name = "Sample") + @Table(name = "SAMPLE_TABLE") static class Sample { @Id - Long id; + Integer id; + + String name; + + public Sample() { + } + public Sample(Integer id, String name) { + this.id = id; + this.name = name; + } } }