From ec60a5ae1c53887448be6a25abcaa36ac3384600 Mon Sep 17 00:00:00 2001 From: Jan Schatteman Date: Thu, 21 Dec 2023 23:23:47 +0100 Subject: [PATCH] HHH-14358 - Added test and fix to support null binding for PostgreSQL Signed-off-by: Jan Schatteman --- .../dialect/CockroachLegacyDialect.java | 5 +- .../dialect/PostgreSQLLegacyDialect.java | 8 +-- .../hibernate/dialect/CockroachDialect.java | 2 +- .../hibernate/dialect/PostgreSQLDialect.java | 3 +- .../dialect/PostgreSQLUUIDJdbcType.java | 57 +++++++++++++++++++ ...ValueTest.java => PostgreSQLUUIDTest.java} | 31 +++++++++- .../test/type/PreferredUuidJdbcTypeTest.java | 4 +- 7 files changed, 95 insertions(+), 15 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLUUIDJdbcType.java rename hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/{PostgreSQLUUIDGeneratedValueTest.java => PostgreSQLUUIDTest.java} (70%) diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java index 0e91fb68eb..00f45223a3 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java @@ -63,7 +63,6 @@ import org.hibernate.type.JavaObjectType; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor; import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType; -import org.hibernate.type.descriptor.jdbc.UUIDJdbcType; import org.hibernate.type.descriptor.jdbc.VarbinaryJdbcType; import org.hibernate.type.descriptor.jdbc.VarcharJdbcType; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; @@ -352,7 +351,7 @@ public class CockroachLegacyDialect extends Dialect { // Don't use this type due to https://github.com/pgjdbc/pgjdbc/issues/2862 //jdbcTypeRegistry.addDescriptor( TimestampUtcAsOffsetDateTimeJdbcType.INSTANCE ); if ( driverKind == PostgreSQLDriverKind.PG_JDBC ) { - jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLUUIDJdbcType.INSTANCE ); if ( PgJdbcHelper.isUsable( serviceRegistry ) ) { jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getIntervalJdbcType( serviceRegistry ) ); @@ -376,7 +375,7 @@ public class CockroachLegacyDialect extends Dialect { } } else { - jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLUUIDJdbcType.INSTANCE ); jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingIntervalSecondJdbcType.INSTANCE ); if ( getVersion().isSameOrAfter( 20, 0 ) ) { jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingInetJdbcType.INSTANCE ); diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java index 35f0077c30..5495e2180c 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java @@ -42,7 +42,6 @@ import org.hibernate.dialect.sequence.PostgreSQLSequenceSupport; import org.hibernate.dialect.sequence.SequenceSupport; import org.hibernate.dialect.unique.CreateTableUniqueDelegate; import org.hibernate.dialect.unique.UniqueDelegate; -import org.hibernate.engine.jdbc.Size; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy; import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; @@ -83,7 +82,6 @@ import org.hibernate.type.descriptor.jdbc.ClobJdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor; import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType; -import org.hibernate.type.descriptor.jdbc.UUIDJdbcType; import org.hibernate.type.descriptor.jdbc.XmlJdbcType; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.hibernate.type.descriptor.sql.internal.ArrayDdlTypeImpl; @@ -1370,8 +1368,8 @@ public class PostgreSQLLegacyDialect extends Dialect { } if ( getVersion().isSameOrAfter( 8, 2 ) ) { - // HHH-9562 - jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE ); + // HHH-9562 / HHH-14358 + jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLUUIDJdbcType.INSTANCE ); if ( getVersion().isSameOrAfter( 9, 2 ) ) { if ( getVersion().isSameOrAfter( 9, 4 ) ) { if ( PgJdbcHelper.isUsable( serviceRegistry ) ) { @@ -1398,7 +1396,7 @@ public class PostgreSQLLegacyDialect extends Dialect { jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLStructCastingJdbcType.INSTANCE ); if ( getVersion().isSameOrAfter( 8, 2 ) ) { - jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLUUIDJdbcType.INSTANCE ); if ( getVersion().isSameOrAfter( 9, 2 ) ) { if ( getVersion().isSameOrAfter( 9, 4 ) ) { jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLCastingJsonJdbcType.JSONB_INSTANCE ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java index 19f95eb552..b82e9e9cfd 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java @@ -369,7 +369,7 @@ public class CockroachDialect extends Dialect { // Don't use this type due to https://github.com/pgjdbc/pgjdbc/issues/2862 //jdbcTypeRegistry.addDescriptor( TimestampUtcAsOffsetDateTimeJdbcType.INSTANCE ); if ( driverKind == PostgreSQLDriverKind.PG_JDBC ) { - jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE ); + jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLUUIDJdbcType.INSTANCE ); if ( PgJdbcHelper.isUsable( serviceRegistry ) ) { jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getIntervalJdbcType( serviceRegistry ) ); jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getInetJdbcType( serviceRegistry ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index e2e79b7f34..29af43d2a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -84,7 +84,6 @@ import org.hibernate.type.descriptor.jdbc.ClobJdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor; import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType; -import org.hibernate.type.descriptor.jdbc.UUIDJdbcType; import org.hibernate.type.descriptor.jdbc.XmlJdbcType; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.hibernate.type.descriptor.sql.internal.ArrayDdlTypeImpl; @@ -1417,7 +1416,6 @@ public class PostgreSQLDialect extends Dialect { //jdbcTypeRegistry.addDescriptor( TimestampUtcAsOffsetDateTimeJdbcType.INSTANCE ); jdbcTypeRegistry.addDescriptor( XmlJdbcType.INSTANCE ); - jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE ); // HHH-9562 if ( driverKind == PostgreSQLDriverKind.PG_JDBC ) { if ( PgJdbcHelper.isUsable( serviceRegistry ) ) { jdbcTypeRegistry.addDescriptorIfAbsent( PgJdbcHelper.getInetJdbcType( serviceRegistry ) ); @@ -1453,6 +1451,7 @@ public class PostgreSQLDialect extends Dialect { ); jdbcTypeRegistry.addDescriptor( new PostgreSQLEnumJdbcType() ); + jdbcTypeRegistry.addDescriptor( PostgreSQLUUIDJdbcType.INSTANCE ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLUUIDJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLUUIDJdbcType.java new file mode 100644 index 0000000000..3f2c16111f --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLUUIDJdbcType.java @@ -0,0 +1,57 @@ +/* + * 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.dialect; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Types; +import java.util.UUID; + +import org.hibernate.type.descriptor.ValueBinder; +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.JavaType; +import org.hibernate.type.descriptor.jdbc.BasicBinder; +import org.hibernate.type.descriptor.jdbc.UUIDJdbcType; + +/** + * @author Jan Schatteman + */ +public class PostgreSQLUUIDJdbcType extends UUIDJdbcType { + + /** + * Singleton access + */ + public static final PostgreSQLUUIDJdbcType INSTANCE = new PostgreSQLUUIDJdbcType(); + + @Override + public ValueBinder getBinder(JavaType javaType) { + return new BasicBinder<>( javaType, this ) { + @Override + protected void doBindNull(PreparedStatement st, int index, WrapperOptions options) throws SQLException { + st.setNull( index, getJdbcType().getJdbcTypeCode(), "uuid" ); + } + + @Override + protected void doBindNull(CallableStatement st, String name, WrapperOptions options) throws SQLException { + st.setNull( name, getJdbcType().getJdbcTypeCode(), "uuid" ); + } + + @Override + protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) + throws SQLException { + st.setObject( index, getJavaType().unwrap( value, UUID.class, options ) ); + } + + @Override + protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) + throws SQLException { + st.setObject( name, getJavaType().unwrap( value, UUID.class, options ) ); + } + }; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/PostgreSQLUUIDGeneratedValueTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/PostgreSQLUUIDTest.java similarity index 70% rename from hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/PostgreSQLUUIDGeneratedValueTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/PostgreSQLUUIDTest.java index 893bd861bc..f7392fe1ea 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/PostgreSQLUUIDGeneratedValueTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/id/uuid/PostgreSQLUUIDTest.java @@ -11,15 +11,19 @@ import java.util.UUID; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; +import jakarta.persistence.Query; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.query.NativeQuery; import org.hibernate.type.StandardBasicTypes; import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.RequiresDialect; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -31,9 +35,9 @@ import static org.hamcrest.Matchers.notNullValue; * @author Vlad Mihalcea */ @RequiresDialect(value = PostgreSQLDialect.class) -@DomainModel(annotatedClasses = { PostgreSQLUUIDGeneratedValueTest.Book.class }) +@DomainModel(annotatedClasses = { PostgreSQLUUIDTest.Book.class }) @SessionFactory -public class PostgreSQLUUIDGeneratedValueTest { +public class PostgreSQLUUIDTest { private UUID id; @@ -49,6 +53,15 @@ public class PostgreSQLUUIDGeneratedValueTest { assertThat( id, notNullValue() ); } + @AfterEach + void tearDown(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createMutationQuery( "delete from Book" ).executeUpdate(); + } + ); + } + @Test public void testJPQL(SessionFactoryScope scope) { scope.inTransaction( session -> { @@ -77,6 +90,20 @@ public class PostgreSQLUUIDGeneratedValueTest { } ); } + @Test + @JiraKey( value = "HHH-14358" ) + public void testUUIDNullBinding(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Query query = session.createQuery( "SELECT b FROM Book b WHERE :id is null or b.id = :id", Book.class ); + query.setParameter("id", null); + List results = Assertions.assertDoesNotThrow( query::getResultList, + "Should not throw a PSQLException of type \"could not determine data type of parameter\" " ); + Assertions.assertEquals( 1, results.size() ); + } + ); + } + @Entity(name = "Book") static class Book { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/type/PreferredUuidJdbcTypeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/PreferredUuidJdbcTypeTest.java index 192a50705e..610bf9e216 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/type/PreferredUuidJdbcTypeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/PreferredUuidJdbcTypeTest.java @@ -4,9 +4,9 @@ import java.util.UUID; import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.cfg.AvailableSettings; -import org.hibernate.dialect.CockroachDialect; import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.PostgreSQLDialect; +import org.hibernate.dialect.PostgreSQLUUIDJdbcType; import org.hibernate.dialect.SQLServerDialect; import org.hibernate.metamodel.spi.MappingMetamodelImplementor; import org.hibernate.persister.entity.EntityPersister; @@ -55,7 +55,7 @@ public class PreferredUuidJdbcTypeTest { assertThat( uuidJdbcType ).isEqualTo( CharJdbcType.INSTANCE ); final JdbcType uuidType = jdbcTypeRegistry.getDescriptor( SqlTypes.UUID ); - assertThat( uuidType ).isOfAnyClassIn( UUIDJdbcType.class ); + assertThat( uuidType ).isOfAnyClassIn( UUIDJdbcType.class, PostgreSQLUUIDJdbcType.class ); // a simple duration field with no overrides - so should be using a default JdbcType assertThat( entityDescriptor.findAttributeMapping( "uuid" )