HHH-14358 - Added test and fix to support null binding for PostgreSQL

Signed-off-by: Jan Schatteman <jschatte@redhat.com>
This commit is contained in:
Jan Schatteman 2023-12-21 23:23:47 +01:00 committed by Christian Beikov
parent 3ac0b8cb5e
commit ec60a5ae1c
7 changed files with 95 additions and 15 deletions

View File

@ -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 );

View File

@ -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 );

View File

@ -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 ) );

View File

@ -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

View File

@ -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 <X> ValueBinder<X> getBinder(JavaType<X> 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 ) );
}
};
}
}

View File

@ -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 {

View File

@ -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" )