HHH-16317 Don't use InstantAsTimestampWithTimeZoneJdbcType for PG-JDBC and MSSQL due to a bug

This commit is contained in:
Christian Beikov 2023-03-22 21:36:45 +01:00
parent 41bec6d5f9
commit eb9e16c83f
7 changed files with 142 additions and 15 deletions

View File

@ -72,7 +72,6 @@ import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.type.JavaObjectType;
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsOffsetDateTimeJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
@ -334,7 +333,8 @@ public class CockroachLegacyDialect extends Dialect {
protected void contributeCockroachTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration()
.getJdbcTypeRegistry();
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, TimestampUtcAsOffsetDateTimeJdbcType.INSTANCE );
// 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 );
if ( PgJdbcHelper.isUsable( serviceRegistry ) ) {

View File

@ -91,7 +91,6 @@ import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsOffsetDateTimeJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
@ -108,9 +107,6 @@ import jakarta.persistence.TemporalType;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.query.sqm.TemporalUnit.DAY;
import static org.hibernate.query.sqm.TemporalUnit.EPOCH;
import static org.hibernate.query.sqm.TemporalUnit.MONTH;
import static org.hibernate.query.sqm.TemporalUnit.QUARTER;
import static org.hibernate.query.sqm.TemporalUnit.YEAR;
import static org.hibernate.type.SqlTypes.ARRAY;
import static org.hibernate.type.SqlTypes.BINARY;
import static org.hibernate.type.SqlTypes.BLOB;
@ -1331,7 +1327,8 @@ public class PostgreSQLLegacyDialect extends Dialect {
// dialect uses oid for Blobs, byte arrays cannot be used.
jdbcTypeRegistry.addDescriptor( Types.BLOB, BlobJdbcType.BLOB_BINDING );
jdbcTypeRegistry.addDescriptor( Types.CLOB, ClobJdbcType.CLOB_BINDING );
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, TimestampUtcAsOffsetDateTimeJdbcType.INSTANCE );
// Don't use this type due to https://github.com/pgjdbc/pgjdbc/issues/2862
//jdbcTypeRegistry.addDescriptor( TimestampUtcAsOffsetDateTimeJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptor( XmlJdbcType.INSTANCE );
if ( driverKind == PostgreSQLDriverKind.PG_JDBC ) {

View File

@ -65,6 +65,7 @@ import org.hibernate.type.BasicTypeRegistry;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsJdbcTimestampJdbcType;
import org.hibernate.type.descriptor.jdbc.TinyIntAsSmallIntJdbcType;
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
import org.hibernate.type.descriptor.jdbc.XmlJdbcType;
@ -267,6 +268,9 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
super.contributeTypes( typeContributions, serviceRegistry );
// Need to bind as java.sql.Timestamp because reading OffsetDateTime from a "datetime2" column fails
typeContributions.contributeJdbcType( TimestampUtcAsJdbcTimestampJdbcType.INSTANCE );
typeContributions.getTypeConfiguration().getJdbcTypeRegistry().addDescriptor(
Types.TINYINT,
TinyIntAsSmallIntJdbcType.INSTANCE

View File

@ -59,7 +59,6 @@ import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.type.JavaObjectType;
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsOffsetDateTimeJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
@ -79,7 +78,6 @@ import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtract
import static org.hibernate.query.sqm.TemporalUnit.DAY;
import static org.hibernate.query.sqm.TemporalUnit.EPOCH;
import static org.hibernate.query.sqm.TemporalUnit.NATIVE;
import static org.hibernate.query.sqm.TemporalUnit.YEAR;
import static org.hibernate.type.SqlTypes.ARRAY;
import static org.hibernate.type.SqlTypes.BINARY;
import static org.hibernate.type.SqlTypes.BLOB;
@ -354,7 +352,8 @@ public class CockroachDialect extends Dialect {
protected void contributeCockroachTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration()
.getJdbcTypeRegistry();
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, TimestampUtcAsOffsetDateTimeJdbcType.INSTANCE );
// 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 );
if ( PgJdbcHelper.isUsable( serviceRegistry ) ) {

View File

@ -80,7 +80,6 @@ import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsOffsetDateTimeJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
@ -97,9 +96,6 @@ import jakarta.persistence.TemporalType;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.query.sqm.TemporalUnit.DAY;
import static org.hibernate.query.sqm.TemporalUnit.EPOCH;
import static org.hibernate.query.sqm.TemporalUnit.MONTH;
import static org.hibernate.query.sqm.TemporalUnit.QUARTER;
import static org.hibernate.query.sqm.TemporalUnit.YEAR;
import static org.hibernate.type.SqlTypes.ARRAY;
import static org.hibernate.type.SqlTypes.BINARY;
import static org.hibernate.type.SqlTypes.BLOB;
@ -1339,7 +1335,8 @@ public class PostgreSQLDialect extends Dialect {
// dialect uses oid for Blobs, byte arrays cannot be used.
jdbcTypeRegistry.addDescriptor( Types.BLOB, BlobJdbcType.BLOB_BINDING );
jdbcTypeRegistry.addDescriptor( Types.CLOB, ClobJdbcType.CLOB_BINDING );
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, TimestampUtcAsOffsetDateTimeJdbcType.INSTANCE );
// Don't use this type due to https://github.com/pgjdbc/pgjdbc/issues/2862
//jdbcTypeRegistry.addDescriptor( TimestampUtcAsOffsetDateTimeJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptor( XmlJdbcType.INSTANCE );
if ( driverKind == PostgreSQLDriverKind.PG_JDBC ) {

View File

@ -73,6 +73,7 @@ import org.hibernate.type.BasicTypeRegistry;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsJdbcTimestampJdbcType;
import org.hibernate.type.descriptor.jdbc.TinyIntAsSmallIntJdbcType;
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
import org.hibernate.type.descriptor.jdbc.XmlJdbcType;
@ -274,6 +275,9 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
super.contributeTypes( typeContributions, serviceRegistry );
// Need to bind as java.sql.Timestamp because reading OffsetDateTime from a "datetime2" column fails
typeContributions.contributeJdbcType( TimestampUtcAsJdbcTimestampJdbcType.INSTANCE );
typeContributions.getTypeConfiguration().getJdbcTypeRegistry().addDescriptor(
Types.TINYINT,
TinyIntAsSmallIntJdbcType.INSTANCE

View File

@ -0,0 +1,126 @@
/*
* 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.type;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.util.TimeZone;
import org.hibernate.testing.jdbc.SharedDriverManagerConnectionProviderImpl;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Access;
import jakarta.persistence.AccessType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import static org.junit.jupiter.api.Assertions.assertEquals;
@DomainModel(
annotatedClasses = { InstantTimestampWithoutTimezoneTest.SomeEntity.class }
)
@SessionFactory
@JiraKey( "HHH-16317" )
public class InstantTimestampWithoutTimezoneTest {
@Test
@Disabled("Just for local testing")
public void testJdbc(SessionFactoryScope scope) {
TimeZone timeZone = TimeZone.getDefault();
try {
TimeZone.setDefault( TimeZone.getTimeZone( "GMT+1" ) );
SharedDriverManagerConnectionProviderImpl.getInstance().reset();
scope.inTransaction(
session -> {
session.doWork(
connection -> {
try (PreparedStatement preparedStatement = connection.prepareStatement(
"insert into SOMEENTITY (ID,TSDATA) values (1,?)" )) {
preparedStatement.setObject(
1,
OffsetDateTime.parse( "2020-01-01T12:00:00Z" )
);
preparedStatement.executeUpdate();
}
}
);
}
);
scope.inTransaction(
session -> {
session.doWork(
connection -> {
try (PreparedStatement preparedStatement = connection.prepareStatement(
"select e.TSDATA from SOMEENTITY e where e.ID=1" );
ResultSet resultSet = preparedStatement.executeQuery()) {
resultSet.next();
OffsetDateTime offsetDateTime = resultSet.getObject( 1, OffsetDateTime.class );
assertEquals(
OffsetDateTime.parse( "2020-01-01T12:00:00Z" ).toInstant(),
offsetDateTime.toInstant()
);
}
}
);
}
);
}
finally {
TimeZone.setDefault( timeZone );
SharedDriverManagerConnectionProviderImpl.getInstance().reset();
}
}
@Test
public void testNativeQuery(SessionFactoryScope scope) {
TimeZone timeZone = TimeZone.getDefault();
try {
TimeZone.setDefault( TimeZone.getTimeZone( "GMT+1" ) );
SharedDriverManagerConnectionProviderImpl.getInstance().reset();
scope.inTransaction(
session -> {
session.createNativeMutationQuery( "insert into SOMEENTITY (ID,TSDATA) values (2,?)" )
.setParameter( 1, OffsetDateTime.parse( "2020-01-01T12:00:00Z" ).toInstant() )
.executeUpdate();
final Instant instant = session.createNativeQuery(
"select e.TSDATA from SOMEENTITY e where e.ID=2",
Instant.class
).getSingleResult();
assertEquals(
OffsetDateTime.parse( "2020-01-01T12:00:00Z" ).toInstant(),
instant
);
}
);
}
finally {
TimeZone.setDefault( timeZone );
SharedDriverManagerConnectionProviderImpl.getInstance().reset();
}
}
@Entity
@Table(name = "SOMEENTITY")
@Access(AccessType.FIELD)
public static class SomeEntity {
@Id
@Column(name = "ID")
private Integer id;
@Column(name = "TSDATA")
private java.sql.Timestamp tsData;
}
}