diff --git a/documentation/src/main/asciidoc/userguide/appendices/Configurations.adoc b/documentation/src/main/asciidoc/userguide/appendices/Configurations.adoc index 255bc4c3f3..6b56eb2c9f 100644 --- a/documentation/src/main/asciidoc/userguide/appendices/Configurations.adoc +++ b/documentation/src/main/asciidoc/userguide/appendices/Configurations.adoc @@ -295,6 +295,20 @@ Assuming `hibernate.globally_quoted_identifiers` is `true`, this allows the glob `*hibernate.auto_quote_keyword*` (e.g. `true` or `false` (default value)):: Specifies whether to automatically quote any names that are deemed keywords. +==== Time zone storage +`*hibernate.timezone.default_storage*` (e.g. `COLUMN`, `NATIVE`, `AUTO` or `NORMALIZE` (default value)):: +Global setting for configuring the default storage for the time zone information for time zone based types. ++ +`NORMALIZE`::: Does not store the time zone, and instead normalizes timestamps to UTC +`COLUMN`::: Stores the time zone in a separate column; works in conjunction with `@TimeZoneColumn` +`NATIVE`::: Stores the time zone by using the `with time zone` type. Error if `Dialect#getTimeZoneSupport()` is not `NATIVE` +`AUTO`::: Stores the time zone either with `NATIVE` if `Dialect#getTimeZoneSupport()` is `NATIVE`, otherwise uses the `COLUMN` strategy. ++ +The default value is given by the {@link org.hibernate.annotations.TimeZoneStorageType#NORMALIZE}, +meaning that time zone information is not stored by default, but timestamps are normalized instead. ++ +See the discussion https://github.com/hibernate/hibernate-orm/discussions/4201[on GitHub] for additional background info. + ==== Discriminator options `*hibernate.discriminator.implicit_for_joined*` (e.g. `true` or `false` (default value)):: The legacy behavior of Hibernate is to not use discriminators for joined inheritance (Hibernate does not need the discriminator). diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BigDecimalMappingTests.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BigDecimalMappingTests.java index 6671adbe5b..29334a6ec4 100644 --- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BigDecimalMappingTests.java +++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BigDecimalMappingTests.java @@ -16,6 +16,7 @@ import org.hibernate.metamodel.MappingMetamodel; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeDescriptorRegistry; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.SessionFactory; @@ -41,6 +42,8 @@ public class BigDecimalMappingTests { // first, verify the type selections... final MappingMetamodel domainModel = scope.getSessionFactory().getDomainModel(); final EntityPersister entityDescriptor = domainModel.findEntityDescriptor( EntityOfBigDecimals.class ); + final JdbcTypeDescriptorRegistry jdbcTypeRegistry = domainModel.getTypeConfiguration() + .getJdbcTypeDescriptorRegistry(); { final BasicAttributeMapping attribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "wrapper" ); @@ -48,7 +51,7 @@ public class BigDecimalMappingTests { final JdbcMapping jdbcMapping = attribute.getJdbcMapping(); assertThat( jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass(), equalTo( BigDecimal.class ) ); - assertThat( jdbcMapping.getJdbcTypeDescriptor().getJdbcTypeCode(), is( Types.NUMERIC ) ); + assertThat( jdbcMapping.getJdbcTypeDescriptor(), is( jdbcTypeRegistry.getDescriptor( Types.NUMERIC ) ) ); } diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BigIntegerMappingTests.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BigIntegerMappingTests.java index 65ac54aa64..1742a4c8bb 100644 --- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BigIntegerMappingTests.java +++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/BigIntegerMappingTests.java @@ -16,6 +16,7 @@ import org.hibernate.metamodel.MappingMetamodel; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeDescriptorRegistry; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.SessionFactory; @@ -41,6 +42,8 @@ public class BigIntegerMappingTests { // first, verify the type selections... final MappingMetamodel domainModel = scope.getSessionFactory().getDomainModel(); final EntityPersister entityDescriptor = domainModel.findEntityDescriptor( EntityOfBigIntegers.class ); + final JdbcTypeDescriptorRegistry jdbcTypeRegistry = domainModel.getTypeConfiguration() + .getJdbcTypeDescriptorRegistry(); { final BasicAttributeMapping attribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "wrapper" ); @@ -48,7 +51,7 @@ public class BigIntegerMappingTests { final JdbcMapping jdbcMapping = attribute.getJdbcMapping(); assertThat( jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass(), equalTo( BigInteger.class ) ); - assertThat( jdbcMapping.getJdbcTypeDescriptor().getJdbcTypeCode(), is( Types.NUMERIC ) ); + assertThat( jdbcMapping.getJdbcTypeDescriptor(), is( jdbcTypeRegistry.getDescriptor( Types.NUMERIC ) ) ); } diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/ByteMappingTests.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/ByteMappingTests.java index 6b4ca3a28e..d6c1b12ed7 100644 --- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/ByteMappingTests.java +++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/ByteMappingTests.java @@ -16,6 +16,7 @@ import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeDescriptorRegistry; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.SessionFactory; @@ -43,6 +44,8 @@ public class ByteMappingTests { // first, verify the type selections... final MappingMetamodel domainModel = scope.getSessionFactory().getDomainModel(); final EntityPersister entityDescriptor = domainModel.findEntityDescriptor( EntityOfBytes.class ); + final JdbcTypeDescriptorRegistry jdbcTypeRegistry = domainModel.getTypeConfiguration() + .getJdbcTypeDescriptorRegistry(); { final BasicAttributeMapping attribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "wrapper" ); @@ -50,7 +53,7 @@ public class ByteMappingTests { final JdbcMapping jdbcMapping = attribute.getJdbcMapping(); assertThat( jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass(), equalTo( Byte.class ) ); - assertThat( jdbcMapping.getJdbcTypeDescriptor().getJdbcTypeCode(), is( Types.TINYINT ) ); + assertThat( jdbcMapping.getJdbcTypeDescriptor(), is( jdbcTypeRegistry.getDescriptor( Types.TINYINT ) ) ); } { @@ -59,7 +62,7 @@ public class ByteMappingTests { final JdbcMapping jdbcMapping = attribute.getJdbcMapping(); assertThat( jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass(), equalTo( Byte.class ) ); - assertThat( jdbcMapping.getJdbcTypeDescriptor().getJdbcTypeCode(), is( Types.TINYINT ) ); + assertThat( jdbcMapping.getJdbcTypeDescriptor(), is( jdbcTypeRegistry.getDescriptor( Types.TINYINT ) ) ); } diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/CharacterArrayNationalizedMappingTests.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/CharacterArrayNationalizedMappingTests.java index 20c95896a5..f30de2125c 100644 --- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/CharacterArrayNationalizedMappingTests.java +++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/CharacterArrayNationalizedMappingTests.java @@ -19,6 +19,7 @@ import org.hibernate.metamodel.MappingMetamodel; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeDescriptorRegistry; import org.hibernate.testing.orm.junit.DialectFeatureChecks; import org.hibernate.testing.orm.junit.DomainModel; @@ -29,6 +30,7 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; /** * @see CharacterArrayMappingTests @@ -43,6 +45,8 @@ public class CharacterArrayNationalizedMappingTests { public void verifyMappings(SessionFactoryScope scope) { final MappingMetamodel domainModel = scope.getSessionFactory().getDomainModel(); final EntityPersister entityDescriptor = domainModel.findEntityDescriptor( EntityWithCharArrays.class ); + final JdbcTypeDescriptorRegistry jdbcTypeRegistry = domainModel.getTypeConfiguration() + .getJdbcTypeDescriptorRegistry(); final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect(); final NationalizationSupport nationalizationSupport = dialect.getNationalizationSupport(); @@ -50,26 +54,26 @@ public class CharacterArrayNationalizedMappingTests { { final BasicAttributeMapping attributeMapping = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "primitiveNVarchar" ); final JdbcMapping jdbcMapping = attributeMapping.getJdbcMapping(); - assertThat( jdbcMapping.getJdbcTypeDescriptor().getJdbcTypeCode(), equalTo( nationalizationSupport.getVarcharVariantCode() ) ); + assertThat( jdbcMapping.getJdbcTypeDescriptor(), is( jdbcTypeRegistry.getDescriptor( nationalizationSupport.getVarcharVariantCode() ) ) ); } { final BasicAttributeMapping attributeMapping = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "wrapperNVarchar" ); final JdbcMapping jdbcMapping = attributeMapping.getJdbcMapping(); - assertThat( jdbcMapping.getJdbcTypeDescriptor().getJdbcTypeCode(), equalTo( nationalizationSupport.getVarcharVariantCode() ) ); + assertThat( jdbcMapping.getJdbcTypeDescriptor(), is( jdbcTypeRegistry.getDescriptor( nationalizationSupport.getVarcharVariantCode() ) ) ); } { final BasicAttributeMapping attributeMapping = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "primitiveNClob" ); final JdbcMapping jdbcMapping = attributeMapping.getJdbcMapping(); - assertThat( jdbcMapping.getJdbcTypeDescriptor().getJdbcTypeCode(), equalTo( nationalizationSupport.getClobVariantCode() ) ); + assertThat( jdbcMapping.getJdbcTypeDescriptor(), is( jdbcTypeRegistry.getDescriptor( nationalizationSupport.getClobVariantCode() ) ) ); } { final BasicAttributeMapping attributeMapping = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "wrapperNClob" ); final JdbcMapping jdbcMapping = attributeMapping.getJdbcMapping(); - assertThat( jdbcMapping.getJdbcTypeDescriptor().getJdbcTypeCode(), equalTo( nationalizationSupport.getClobVariantCode() ) ); + assertThat( jdbcMapping.getJdbcTypeDescriptor(), is( jdbcTypeRegistry.getDescriptor( nationalizationSupport.getClobVariantCode() ) ) ); } } diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/DurationMappingTests.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/DurationMappingTests.java index 7383aed50a..9e7b652d09 100644 --- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/DurationMappingTests.java +++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/DurationMappingTests.java @@ -17,6 +17,7 @@ import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeDescriptorRegistry; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.SessionFactory; @@ -25,6 +26,7 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; /** * @author Steve Ebersole @@ -37,11 +39,13 @@ public class DurationMappingTests { public void verifyMappings(SessionFactoryScope scope) { final MappingMetamodel domainModel = scope.getSessionFactory().getDomainModel(); final EntityPersister entityDescriptor = domainModel.findEntityDescriptor( EntityWithDuration.class ); + final JdbcTypeDescriptorRegistry jdbcTypeRegistry = domainModel.getTypeConfiguration() + .getJdbcTypeDescriptorRegistry(); final BasicAttributeMapping duration = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "duration" ); final JdbcMapping jdbcMapping = duration.getJdbcMapping(); assertThat( jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass(), equalTo( Duration.class ) ); - assertThat( jdbcMapping.getJdbcTypeDescriptor().getJdbcTypeCode(), equalTo( Types.NUMERIC ) ); + assertThat( jdbcMapping.getJdbcTypeDescriptor(), is( jdbcTypeRegistry.getDescriptor( Types.NUMERIC ) ) ); scope.inTransaction( (session) -> { diff --git a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/StringNationalizedMappingTests.java b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/StringNationalizedMappingTests.java index 1065ce554e..eb208ffcbe 100644 --- a/documentation/src/test/java/org/hibernate/userguide/mapping/basic/StringNationalizedMappingTests.java +++ b/documentation/src/test/java/org/hibernate/userguide/mapping/basic/StringNationalizedMappingTests.java @@ -6,6 +6,8 @@ */ package org.hibernate.userguide.mapping.basic; +import java.sql.Types; + import jakarta.persistence.Entity; import jakarta.persistence.Id; import jakarta.persistence.Lob; @@ -18,6 +20,7 @@ import org.hibernate.metamodel.MappingMetamodel; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeDescriptorRegistry; import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.SessionFactory; @@ -27,6 +30,7 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; /** * Tests for mapping `double` values @@ -42,6 +46,8 @@ public class StringNationalizedMappingTests { // first, verify the type selections... final MappingMetamodel domainModel = scope.getSessionFactory().getDomainModel(); final EntityPersister entityDescriptor = domainModel.findEntityDescriptor( EntityOfStrings.class ); + final JdbcTypeDescriptorRegistry jdbcTypeRegistry = domainModel.getTypeConfiguration() + .getJdbcTypeDescriptorRegistry(); final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect(); final NationalizationSupport nationalizationSupport = dialect.getNationalizationSupport(); @@ -50,14 +56,14 @@ public class StringNationalizedMappingTests { final BasicAttributeMapping attribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "nstring" ); final JdbcMapping jdbcMapping = attribute.getJdbcMapping(); assertThat( jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass(), equalTo( String.class ) ); - assertThat( jdbcMapping.getJdbcTypeDescriptor().getJdbcTypeCode(), equalTo( nationalizationSupport.getVarcharVariantCode() ) ); + assertThat( jdbcMapping.getJdbcTypeDescriptor(), is( jdbcTypeRegistry.getDescriptor( nationalizationSupport.getVarcharVariantCode() ) ) ); } { final BasicAttributeMapping attribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "nclobString" ); final JdbcMapping jdbcMapping = attribute.getJdbcMapping(); assertThat( jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass(), equalTo( String.class ) ); - assertThat( jdbcMapping.getJdbcTypeDescriptor().getJdbcTypeCode(), equalTo( nationalizationSupport.getClobVariantCode() ) ); + assertThat( jdbcMapping.getJdbcTypeDescriptor(), is( jdbcTypeRegistry.getDescriptor( nationalizationSupport.getClobVariantCode() ) ) ); } diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CUBRIDDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CUBRIDDialect.java index ee9b790d2d..3d33492f1b 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CUBRIDDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CUBRIDDialect.java @@ -10,6 +10,7 @@ import org.hibernate.HibernateException; import org.hibernate.cfg.Environment; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.OracleDialect; +import org.hibernate.dialect.TimeZoneSupport; import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.community.dialect.identity.CUBRIDIdentityColumnSupport; import org.hibernate.dialect.identity.IdentityColumnSupport; @@ -372,8 +373,8 @@ public class CUBRIDDialect extends Dialect { } @Override - public boolean supportsTimezoneTypes() { - return true; + public TimeZoneSupport getTimeZoneSupport() { + return TimeZoneSupport.NATIVE; } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/FirebirdDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/FirebirdDialect.java index 9f717403e0..e90f15350d 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/FirebirdDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/FirebirdDialect.java @@ -10,6 +10,7 @@ import org.hibernate.HibernateException; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.dialect.BooleanDecoder; import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.TimeZoneSupport; import org.hibernate.query.NullOrdering; import org.hibernate.cfg.Environment; import org.hibernate.dialect.function.CommonFunctionFactory; @@ -57,11 +58,9 @@ import org.hibernate.type.BasicTypeRegistry; import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptor; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeDescriptorRegistry; -import org.hibernate.type.spi.TypeConfiguration; import java.sql.DatabaseMetaData; import java.sql.SQLException; -import java.sql.Timestamp; import java.sql.Types; import java.time.temporal.TemporalAccessor; import java.util.Arrays; @@ -170,8 +169,8 @@ public class FirebirdDialect extends Dialect { } @Override - public boolean supportsTimezoneTypes() { - return getVersion() >= 400; + public TimeZoneSupport getTimeZoneSupport() { + return getVersion() >= 400 ? TimeZoneSupport.NATIVE : TimeZoneSupport.NONE; } @Override @@ -315,7 +314,7 @@ public class FirebirdDialect extends Dialect { @Override public String currentLocalTime() { - if ( supportsTimezoneTypes() ) { + if ( getTimeZoneSupport() == TimeZoneSupport.NATIVE ) { return "localtime"; } else { @@ -325,7 +324,7 @@ public class FirebirdDialect extends Dialect { @Override public String currentLocalTimestamp() { - if ( supportsTimezoneTypes() ) { + if ( getTimeZoneSupport() == TimeZoneSupport.NATIVE ) { return "localtimestamp"; } else { diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/IngresDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/IngresDialect.java index 5b1fae7e4d..6636c73bb4 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/IngresDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/IngresDialect.java @@ -9,6 +9,7 @@ package org.hibernate.community.dialect; import org.hibernate.cfg.Environment; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.MySQLDialect; +import org.hibernate.dialect.TimeZoneSupport; import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.community.dialect.identity.Ingres10IdentityColumnSupport; @@ -318,8 +319,8 @@ public class IngresDialect extends Dialect { } @Override - public boolean supportsTimezoneTypes() { - return true; + public TimeZoneSupport getTimeZoneSupport() { + return TimeZoneSupport.NATIVE; } @Override diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseAnywhereDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseAnywhereDialect.java index 3bb8dea977..d76c621241 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseAnywhereDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseAnywhereDialect.java @@ -10,6 +10,7 @@ package org.hibernate.community.dialect; import org.hibernate.LockOptions; import org.hibernate.dialect.RowLockStrategy; import org.hibernate.dialect.SybaseDialect; +import org.hibernate.dialect.TimeZoneSupport; import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.community.dialect.identity.SybaseAnywhereIdentityColumnSupport; import org.hibernate.dialect.pagination.LimitHandler; @@ -80,8 +81,8 @@ public class SybaseAnywhereDialect extends SybaseDialect { } @Override - public boolean supportsTimezoneTypes() { - return true; + public TimeZoneSupport getTimeZoneSupport() { + return TimeZoneSupport.NATIVE; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/TimeZoneStorageStrategy.java b/hibernate-core/src/main/java/org/hibernate/TimeZoneStorageStrategy.java new file mode 100644 index 0000000000..fe5afcf8e4 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/TimeZoneStorageStrategy.java @@ -0,0 +1,30 @@ +/* + * 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 . + */ +package org.hibernate; + +/** + * Describes the storage strategies understood by Hibernate. + * + * @author Christian Beikov + * @author Steve Ebersole + * @author Andrea Boriero + */ +@Incubating +public enum TimeZoneStorageStrategy { + /** + * Stores the time zone through the "with time zone" types which retain the information. + */ + NATIVE, + /** + * Stores the time zone in a separate column. + */ + COLUMN, + /** + * Doesn't store the time zone, but instead normalizes to UTC. + */ + NORMALIZE; +} diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/TimeZoneColumn.java b/hibernate-core/src/main/java/org/hibernate/annotations/TimeZoneColumn.java new file mode 100644 index 0000000000..1785d1389d --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/annotations/TimeZoneColumn.java @@ -0,0 +1,48 @@ +/* + * 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 . + */ +package org.hibernate.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.hibernate.Incubating; + +import jakarta.persistence.Column; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; + +/** + * Specifies the column name and type to use for storing the time zone information. + * The annotation can be used in conjunction with the TimeZoneStorageType.AUTO and + * TimeZoneStorageType.COLUMN. The column is simply ignored if TimeZoneStorageType.AUTO + * is used and the database supports native time zone storage. + * + * @author Christian Beikov + * @author Steve Ebersole + * @author Andrea Boriero + * @see TimeZoneStorage + * @see TimeZoneStorageType#COLUMN + * @see TimeZoneStorageType#AUTO + */ +@Incubating +@Retention(RetentionPolicy.RUNTIME) +@Target({ FIELD, METHOD }) +public @interface TimeZoneColumn { + + /** + * The column for the time zone information. + */ + Column column(); + + /** + * The storage type for the time zone information. + */ + TimeZoneType type() default TimeZoneType.OFFSET; + +} diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/TimeZoneStorage.java b/hibernate-core/src/main/java/org/hibernate/annotations/TimeZoneStorage.java new file mode 100644 index 0000000000..085e5bffea --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/annotations/TimeZoneStorage.java @@ -0,0 +1,51 @@ +/* + * 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 . + */ +package org.hibernate.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.hibernate.Incubating; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; + +/** + * Specifies how the time zone information of a persistent property or field should be persisted. + * The TimeZoneStorage annotation may be used in conjunction with the Basic annotation, or in + * conjunction with the ElementCollection annotation when the + * element collection value is of basic type. If the TimeZoneStorage annotation is not + * used, the TimeZoneStorageType value is assumed to be NORMALIZED. + * + *
+ *   Example:
+ *
+ *   @Entity public class Person {
+ *       public OffsetDateTime getBirthDateTimeNormalized() {...}
+ *
+ *       @TimeZoneStorage
+ *       @TimeZoneColumn(column = @Column(...))
+ *       public OffsetDateTime getBirthDateTimeNativeOrColumn() {...}
+ *       ...
+ *   }
+ * 
+ * + * @author Christian Beikov + * @author Steve Ebersole + * @author Andrea Boriero + * @see TimeZoneColumn + */ +@Incubating +@Retention(RetentionPolicy.RUNTIME) +@Target({ FIELD, METHOD }) +public @interface TimeZoneStorage { + /** + * The storage strategy for the time zone information. + */ + TimeZoneStorageType value() default TimeZoneStorageType.AUTO; +} diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/TimeZoneStorageType.java b/hibernate-core/src/main/java/org/hibernate/annotations/TimeZoneStorageType.java new file mode 100644 index 0000000000..393ca19413 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/annotations/TimeZoneStorageType.java @@ -0,0 +1,38 @@ +/* + * 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 . + */ +package org.hibernate.annotations; + +import org.hibernate.Incubating; +import org.hibernate.dialect.Dialect; + +/** + * Describes the storage for the time zone information for time zone based types. + * + * @author Christian Beikov + * @author Steve Ebersole + * @author Andrea Boriero + */ +@Incubating +public enum TimeZoneStorageType { + /** + * Stores the time zone by using the "with time zone" type. Error if {@link Dialect#getTimeZoneSupport()} is not {@link org.hibernate.dialect.TimeZoneSupport#NATIVE}. + */ + NATIVE, + /** + * Does not store the time zone, and instead normalizes timestamps to UTC. + */ + NORMALIZE, + /** + * Stores the time zone in a separate column; works in conjunction with {@link TimeZoneColumn}. + */ + COLUMN, + /** + * Stores the time zone either with {@link #NATIVE} if {@link Dialect#getTimeZoneSupport()} is {@link org.hibernate.dialect.TimeZoneSupport#NATIVE}, otherwise uses the {@link #COLUMN} strategy. + */ + AUTO + +} diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/TimeZoneType.java b/hibernate-core/src/main/java/org/hibernate/annotations/TimeZoneType.java new file mode 100644 index 0000000000..737cbf3f55 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/annotations/TimeZoneType.java @@ -0,0 +1,30 @@ +/* + * 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 . + */ +package org.hibernate.annotations; + +import org.hibernate.Incubating; + +/** + * The type of storage to use for the time zone information. + * + * @author Christian Beikov + * @author Steve Ebersole + * @author Andrea Boriero + */ +@Incubating +public enum TimeZoneType { + + /** + * Stores the time zone id as String. + */ + ZONE_ID, + /** + * Stores the offset seconds of a timestamp as Integer. + */ + OFFSET; + +} diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/internal/NoJdbcTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/annotations/internal/NoJdbcTypeDescriptor.java index 9ef9a838aa..c795a525ab 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/internal/NoJdbcTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/internal/NoJdbcTypeDescriptor.java @@ -17,11 +17,6 @@ public class NoJdbcTypeDescriptor implements JdbcTypeDescriptor { throw new UnsupportedOperationException(); } - @Override - public boolean canBeRemapped() { - throw new UnsupportedOperationException(); - } - @Override public ValueBinder getBinder(JavaTypeDescriptor javaTypeDescriptor) { throw new UnsupportedOperationException(); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java index 93b9ae6972..f3f2671b3e 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/MetadataBuilderImpl.java @@ -16,7 +16,9 @@ import jakarta.persistence.SharedCacheMode; import org.hibernate.HibernateException; import org.hibernate.MultiTenancyStrategy; +import org.hibernate.TimeZoneStorageStrategy; import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.TimeZoneStorageType; import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.boot.CacheRegionDefinition; import org.hibernate.boot.MetadataBuilder; @@ -58,8 +60,10 @@ import org.hibernate.cache.spi.RegionFactory; import org.hibernate.cache.spi.access.AccessType; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.MetadataSourceType; +import org.hibernate.dialect.TimeZoneSupport; import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.StandardConverters; +import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.log.DeprecationLogger; @@ -533,6 +537,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont implements MetadataBuildingOptions, JpaOrmXmlPersistenceUnitDefaultAware { private final StandardServiceRegistry serviceRegistry; private final MappingDefaultsImpl mappingDefaults; + private final TimeZoneStorageStrategy defaultTimezoneStorage; // todo (6.0) : remove bootstrapContext property along with the deprecated methods private BootstrapContext bootstrapContext; @@ -565,6 +570,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont this.mappingDefaults = new MappingDefaultsImpl( serviceRegistry ); + this.defaultTimezoneStorage = resolveTimeZoneStorageStrategy( serviceRegistry, configService ); this.multiTenancyStrategy = MultiTenancyStrategy.determineMultiTenancyStrategy( configService.getSettings() ); this.xmlMappingEnabled = configService.getSetting( @@ -744,6 +750,11 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont return mappingDefaults; } + @Override + public TimeZoneStorageStrategy getDefaultTimeZoneStorage() { + return defaultTimezoneStorage; + } + @Override public List getBasicTypeRegistrations() { return basicTypeRegistrations; @@ -896,4 +907,54 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont this.bootstrapContext = bootstrapContext; } } + + private static TimeZoneStorageStrategy resolveTimeZoneStorageStrategy( + StandardServiceRegistry serviceRegistry, + ConfigurationService configService) { + final TimeZoneStorageType configuredTimeZoneStorageType = configService.getSetting( + AvailableSettings.TIMEZONE_DEFAULT_STORAGE, + TimeZoneStorageType.class, + null + ); + final TimeZoneStorageStrategy resolvedTimezoneStorage; + // For now, we default to NORMALIZE as that is the Hibernate 5.x behavior + if ( configuredTimeZoneStorageType == null ) { + resolvedTimezoneStorage = TimeZoneStorageStrategy.NORMALIZE; + } + else { + final TimeZoneSupport timeZoneSupport = serviceRegistry.getService( JdbcServices.class ) + .getDialect() + .getTimeZoneSupport(); + switch ( configuredTimeZoneStorageType ) { + case NATIVE: + if ( timeZoneSupport != TimeZoneSupport.NATIVE ) { + throw new HibernateException( "The configured time zone storage type NATIVE is not supported with the configured dialect" ); + } + resolvedTimezoneStorage = TimeZoneStorageStrategy.NATIVE; + break; + case COLUMN: + resolvedTimezoneStorage = TimeZoneStorageStrategy.COLUMN; + break; + case NORMALIZE: + resolvedTimezoneStorage = TimeZoneStorageStrategy.NORMALIZE; + break; + case AUTO: + switch ( timeZoneSupport ) { + case NATIVE: + resolvedTimezoneStorage = TimeZoneStorageStrategy.NATIVE; + break; + case NORMALIZE: + case NONE: + resolvedTimezoneStorage = TimeZoneStorageStrategy.COLUMN; + break; + default: + throw new HibernateException( "Unsupported time zone support: " + timeZoneSupport ); + } + break; + default: + throw new HibernateException( "Unsupported time zone storage type: " + configuredTimeZoneStorageType ); + } + } + return resolvedTimezoneStorage; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java index 747a01906c..e0a40fce03 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java @@ -27,6 +27,7 @@ import org.hibernate.Interceptor; import org.hibernate.MultiTenancyStrategy; import org.hibernate.SessionEventListener; import org.hibernate.SessionFactoryObserver; +import org.hibernate.TimeZoneStorageStrategy; import org.hibernate.boot.SchemaAutoTooling; import org.hibernate.boot.TempTableDdlTransactionHandling; import org.hibernate.boot.registry.StandardServiceRegistry; @@ -219,6 +220,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { private boolean conventionalJavaConstants; private final boolean omitJoinOfSuperclassTablesEnabled; private final int preferredSqlTypeCodeForBoolean; + private final TimeZoneStorageStrategy defaultTimeZoneStorageStrategy; // Caching private boolean secondLevelCacheEnabled; @@ -422,6 +424,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { CONVENTIONAL_JAVA_CONSTANTS, BOOLEAN, true ); this.omitJoinOfSuperclassTablesEnabled = cfgService.getSetting( OMIT_JOIN_OF_SUPERCLASS_TABLES, BOOLEAN, true ); this.preferredSqlTypeCodeForBoolean = ConfigurationHelper.getPreferredSqlTypeCodeForBoolean( serviceRegistry ); + this.defaultTimeZoneStorageStrategy = context.getMetadataBuildingOptions().getDefaultTimeZoneStorage(); final RegionFactory regionFactory = serviceRegistry.getService( RegionFactory.class ); if ( !NoCachingRegionFactory.class.isInstance( regionFactory ) ) { @@ -1182,6 +1185,11 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions { public int getPreferredSqlTypeCodeForBoolean() { return preferredSqlTypeCodeForBoolean; } + + @Override + public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() { + return defaultTimeZoneStorageStrategy; + } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // In-flight mutation access diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/VersionResolution.java b/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/VersionResolution.java index 9bcd979b19..d6eb03f402 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/VersionResolution.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/process/internal/VersionResolution.java @@ -9,6 +9,7 @@ package org.hibernate.boot.model.process.internal; import java.util.function.Function; import jakarta.persistence.TemporalType; +import org.hibernate.TimeZoneStorageStrategy; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.mapping.BasicValue; import org.hibernate.metamodel.mapping.JdbcMapping; @@ -57,6 +58,11 @@ public class VersionResolution implements BasicValue.Resolution { // if it is a temporal version, it needs to be a TIMESTAMP return TemporalType.TIMESTAMP; } + + @Override + public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() { + return context.getBuildingOptions().getDefaultTimeZoneStorage(); + } } ); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java b/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java index f6cdf8d96f..04f0428435 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/process/spi/MetadataBuildingProcess.java @@ -6,11 +6,15 @@ */ package org.hibernate.boot.model.process.spi; +import java.sql.Types; +import java.time.OffsetDateTime; +import java.time.ZonedDateTime; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Set; +import org.hibernate.TimeZoneStorageStrategy; import org.hibernate.boot.MetadataSources; import org.hibernate.boot.internal.InFlightMetadataCollectorImpl; import org.hibernate.boot.internal.MetadataBuildingContextRootImpl; @@ -42,9 +46,10 @@ import org.hibernate.type.BasicType; import org.hibernate.type.BasicTypeRegistry; import org.hibernate.type.CustomType; import org.hibernate.type.descriptor.java.JavaTypeDescriptor; -import org.hibernate.type.descriptor.java.JavaTypedExpressable; import org.hibernate.type.descriptor.java.spi.JavaTypeDescriptorRegistry; import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptor; +import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeDescriptorRegistry; +import org.hibernate.type.internal.NamedBasicTypeImpl; import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.usertype.UserType; @@ -420,5 +425,37 @@ public class MetadataBuildingProcess { // add explicit application registered types bootstrapContext.getTypeConfiguration() .addBasicTypeRegistrationContributions( options.getBasicTypeRegistrations() ); + + // For NORMALIZE, we replace the standard types that use TIMESTAMP_WITH_TIMEZONE to use TIMESTAMP + if ( options.getDefaultTimeZoneStorage() == TimeZoneStorageStrategy.NORMALIZE ) { + final JdbcTypeDescriptorRegistry jdbcTypeRegistry = bootstrapContext.getTypeConfiguration() + .getJdbcTypeDescriptorRegistry(); + final JavaTypeDescriptorRegistry javaTypeRegistry = bootstrapContext.getTypeConfiguration() + .getJavaTypeDescriptorRegistry(); + final JdbcTypeDescriptor timestampDescriptor = jdbcTypeRegistry.getDescriptor( Types.TIMESTAMP ); + final BasicTypeRegistry basicTypeRegistry = bootstrapContext.getTypeConfiguration().getBasicTypeRegistry(); + final BasicType offsetDateTimeType = new NamedBasicTypeImpl<>( + javaTypeRegistry.getDescriptor( OffsetDateTime.class ), + timestampDescriptor, + "OffsetDateTime" + ); + final BasicType zonedDateTimeType = new NamedBasicTypeImpl<>( + javaTypeRegistry.getDescriptor( ZonedDateTime.class ), + timestampDescriptor, + "ZonedDateTime" + ); + basicTypeRegistry.register( + offsetDateTimeType, + "org.hibernate.type.OffsetDateTimeType", + OffsetDateTime.class.getSimpleName(), + OffsetDateTime.class.getName() + ); + basicTypeRegistry.register( + zonedDateTimeType, + "org.hibernate.type.ZonedDateTimeType", + ZonedDateTime.class.getSimpleName(), + ZonedDateTime.class.getName() + ); + } } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingMetadataBuildingOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingMetadataBuildingOptions.java index 3e215929e6..c34b9ad5d4 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingMetadataBuildingOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingMetadataBuildingOptions.java @@ -11,6 +11,7 @@ import jakarta.persistence.SharedCacheMode; import org.hibernate.HibernateException; import org.hibernate.MultiTenancyStrategy; +import org.hibernate.TimeZoneStorageStrategy; import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.boot.CacheRegionDefinition; import org.hibernate.boot.archive.scan.spi.ScanEnvironment; @@ -55,6 +56,11 @@ public abstract class AbstractDelegatingMetadataBuildingOptions implements Metad return delegate.getMappingDefaults(); } + @Override + public TimeZoneStorageStrategy getDefaultTimeZoneStorage() { + return delegate.getDefaultTimeZoneStorage(); + } + @Override public List getBasicTypeRegistrations() { return delegate.getBasicTypeRegistrations(); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java index 8533f25b79..97de9df9aa 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/AbstractDelegatingSessionFactoryOptions.java @@ -17,6 +17,7 @@ import org.hibernate.EntityNameResolver; import org.hibernate.Interceptor; import org.hibernate.MultiTenancyStrategy; import org.hibernate.SessionFactoryObserver; +import org.hibernate.TimeZoneStorageStrategy; import org.hibernate.boot.SchemaAutoTooling; import org.hibernate.boot.TempTableDdlTransactionHandling; import org.hibernate.boot.registry.StandardServiceRegistry; @@ -468,4 +469,9 @@ public class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOp public int getPreferredSqlTypeCodeForBoolean() { return delegate.getPreferredSqlTypeCodeForBoolean(); } + + @Override + public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() { + return delegate.getDefaultTimeZoneStorageStrategy(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/MetadataBuildingOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/MetadataBuildingOptions.java index a5f96b5c67..b0a64d986e 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/MetadataBuildingOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/MetadataBuildingOptions.java @@ -10,6 +10,7 @@ import java.util.List; import jakarta.persistence.SharedCacheMode; import org.hibernate.MultiTenancyStrategy; +import org.hibernate.TimeZoneStorageStrategy; import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.boot.CacheRegionDefinition; import org.hibernate.boot.archive.scan.spi.ScanEnvironment; @@ -52,6 +53,8 @@ public interface MetadataBuildingOptions { */ MappingDefaults getMappingDefaults(); + TimeZoneStorageStrategy getDefaultTimeZoneStorage(); + default ManagedTypeRepresentationResolver getManagedTypeRepresentationResolver() { // for now always return the standard one return ManagedTypeRepresentationResolverStandard.INSTANCE; diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java index d7ad0962a6..6bfb5b603f 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/SessionFactoryOptions.java @@ -17,6 +17,7 @@ import org.hibernate.EntityNameResolver; import org.hibernate.HibernateException; import org.hibernate.Interceptor; import org.hibernate.MultiTenancyStrategy; +import org.hibernate.TimeZoneStorageStrategy; import org.hibernate.query.NullPrecedence; import org.hibernate.SessionFactoryObserver; import org.hibernate.boot.SchemaAutoTooling; @@ -348,4 +349,6 @@ public interface SessionFactoryOptions extends QueryEngineOptions { boolean isOmitJoinOfSuperclassTablesEnabled(); int getPreferredSqlTypeCodeForBoolean(); + + TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy(); } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java index 5bf01e072b..b03ddee24f 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java @@ -2383,7 +2383,6 @@ public interface AvailableSettings { */ String OMIT_JOIN_OF_SUPERCLASS_TABLES = "hibernate.query.omit_join_of_superclass_tables"; - /** * Global setting identifying the preferred JDBC type code for storing * boolean values. The fallback is to ask the Dialect @@ -2392,6 +2391,21 @@ public interface AvailableSettings { */ String PREFERRED_BOOLEAN_JDBC_TYPE_CODE = "hibernate.type.perferred_boolean_jdbc_type_code"; + /** + * Global setting for configuring the default storage for the time zone information for time zone based types. + *

+ * Possible values are {@link org.hibernate.annotations.TimeZoneStorageType#NORMALIZE}, + * {@link org.hibernate.annotations.TimeZoneStorageType#COLUMN}, + * {@link org.hibernate.annotations.TimeZoneStorageType#NATIVE} + * and {@link org.hibernate.annotations.TimeZoneStorageType#AUTO}. + *

+ * The default value is given by the {@link org.hibernate.annotations.TimeZoneStorageType#NORMALIZE}, + * meaning that time zone information is not stored by default, but timestamps are normalized instead. + * + * @since 6.0 + */ + String TIMEZONE_DEFAULT_STORAGE = "hibernate.timezone.default_storage"; + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Java (javax) Persistence defined settings // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/BasicValueBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/BasicValueBinder.java index 0f84e6b306..a47da9529a 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/BasicValueBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/BasicValueBinder.java @@ -17,6 +17,7 @@ import java.util.function.Function; import org.hibernate.AnnotationException; import org.hibernate.AssertionFailure; import org.hibernate.MappingException; +import org.hibernate.TimeZoneStorageStrategy; import org.hibernate.annotations.AnyDiscriminator; import org.hibernate.annotations.AnyKeyJavaClass; import org.hibernate.annotations.AnyKeyJavaType; @@ -190,6 +191,10 @@ public class BasicValueBinder implements JdbcTypeDescriptorIndicators { public TypeConfiguration getTypeConfiguration() { return buildingContext.getBootstrapContext().getTypeConfiguration(); } + @Override + public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() { + return buildingContext.getBuildingOptions().getDefaultTimeZoneStorage(); + } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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 3bbccfb1e0..dc640e7667 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java @@ -141,8 +141,8 @@ public class CockroachDialect extends Dialect { } @Override - public boolean supportsTimezoneTypes() { - return true; + public TimeZoneSupport getTimeZoneSupport() { + return TimeZoneSupport.NORMALIZE; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java index c21f7eb6e1..d0f75ca750 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java @@ -54,8 +54,8 @@ public class DB2zDialect extends DB2Dialect { } @Override - public boolean supportsTimezoneTypes() { - return getZVersion() > 1000; + public TimeZoneSupport getTimeZoneSupport() { + return getZVersion() > 1000 ? TimeZoneSupport.NATIVE : TimeZoneSupport.NONE; } int getZVersion() { diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index af878d1582..3089b19ac2 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -3726,10 +3726,10 @@ public abstract class Dialect implements ConversionContext { } /** - * Whether the Dialect supports timezone types like {@link Types#TIMESTAMP_WITH_TIMEZONE}. + * How the Dialect supports time zone types like {@link Types#TIMESTAMP_WITH_TIMEZONE}. */ - public boolean supportsTimezoneTypes() { - return false; + public TimeZoneSupport getTimeZoneSupport() { + return TimeZoneSupport.NONE; } /** diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java index 06feebbe6b..5a82d81d0d 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java @@ -52,6 +52,7 @@ import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorH2 import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl; import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl; import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; + import org.jboss.logging.Logger; import static org.hibernate.query.TemporalUnit.SECOND; @@ -147,6 +148,11 @@ public class H2Dialect extends Dialect { ); } + public boolean hasDstBug() { + // H2 1.4.200 has a bug: https://github.com/h2database/h2database/issues/3184 + return getVersion() == 104200; + } + @Override public int getVersion() { return version; @@ -265,8 +271,8 @@ public class H2Dialect extends Dialect { } @Override - public boolean supportsTimezoneTypes() { - return true; + public TimeZoneSupport getTimeZoneSupport() { + return TimeZoneSupport.NATIVE; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java index 81ad42e971..f976ed3f69 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -567,8 +567,8 @@ public class OracleDialect extends Dialect { } @Override - public boolean supportsTimezoneTypes() { - return getVersion() >= 900; + public TimeZoneSupport getTimeZoneSupport() { + return getVersion() >= 900 ? TimeZoneSupport.NATIVE : TimeZoneSupport.NONE; } protected void registerBinaryTypeMappings() { 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 3c216f97d0..a74ade1b96 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -69,7 +69,6 @@ import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaTypeDescriptor; import org.hibernate.type.descriptor.jdbc.BlobJdbcTypeDescriptor; import org.hibernate.type.descriptor.jdbc.ClobJdbcTypeDescriptor; -import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptor; import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcTypeDescriptor; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeDescriptorRegistry; @@ -287,8 +286,8 @@ public class PostgreSQLDialect extends Dialect { } @Override - public boolean supportsTimezoneTypes() { - return true; + public TimeZoneSupport getTimeZoneSupport() { + return TimeZoneSupport.NORMALIZE; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java index 0b1e1e4f3f..466dff9bce 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java @@ -149,8 +149,8 @@ public class SQLServerDialect extends AbstractTransactSQLDialect { } @Override - public boolean supportsTimezoneTypes() { - return getVersion() >= 10; + public TimeZoneSupport getTimeZoneSupport() { + return getVersion() >= 10 ? TimeZoneSupport.NATIVE : TimeZoneSupport.NONE; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java index 4bf5f79b8b..c953165ab9 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java @@ -45,7 +45,6 @@ import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptor; import org.hibernate.type.descriptor.jdbc.NClobJdbcTypeDescriptor; import org.hibernate.type.descriptor.jdbc.ObjectNullAsNullTypeJdbcTypeDescriptor; import org.hibernate.type.descriptor.jdbc.SmallIntJdbcTypeDescriptor; -import org.hibernate.type.descriptor.jdbc.TinyIntJdbcTypeDescriptor; import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeDescriptorRegistry; import java.sql.DatabaseMetaData; @@ -172,9 +171,10 @@ public class SybaseDialect extends AbstractTransactSQLDialect { // The jTDS driver doesn't support the JDBC4 signatures using 'long length' for stream bindings jdbcTypeRegistry.addDescriptor( Types.CLOB, ClobJdbcTypeDescriptor.CLOB_BINDING ); - jdbcTypeRegistry.addDescriptor( Types.NCLOB, NClobJdbcTypeDescriptor.NCLOB_BINDING ); - // The jTDS driver doesn't support the JDBC4 setNString method - jdbcTypeRegistry.addDescriptor( Types.NVARCHAR, NClobJdbcTypeDescriptor.NCLOB_BINDING ); + + // The jTDS driver doesn't support nationalized types + jdbcTypeRegistry.addDescriptor( Types.NCLOB, ClobJdbcTypeDescriptor.CLOB_BINDING ); + jdbcTypeRegistry.addDescriptor( Types.NVARCHAR, ClobJdbcTypeDescriptor.CLOB_BINDING ); } else { // Some Sybase drivers cannot support getClob. See HHH-7889 diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/TimeZoneSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/TimeZoneSupport.java new file mode 100644 index 0000000000..3810828862 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/dialect/TimeZoneSupport.java @@ -0,0 +1,30 @@ +/* + * 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 . + */ +package org.hibernate.dialect; + +import org.hibernate.Incubating; + +/** + * Describes the support for "with time zone" types. + * + * @author Christian Beikov + */ +@Incubating +public enum TimeZoneSupport { + /** + * The "with time zone" types retain the time zone information. + */ + NATIVE, + /** + * The "with time zone" types normalize to UTC. + */ + NORMALIZE, + /** + * No support for "with time zone" types. + */ + NONE; +} diff --git a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java index b86f7dce22..d14c71b6b8 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/FastSessionServices.java @@ -17,6 +17,7 @@ import jakarta.persistence.PessimisticLockScope; import org.hibernate.CacheMode; import org.hibernate.FlushMode; import org.hibernate.LockOptions; +import org.hibernate.TimeZoneStorageStrategy; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.cfg.BaselineSessionEventsListenerBuilder; @@ -66,7 +67,6 @@ import org.hibernate.jpa.internal.util.ConfigurationHelper; import org.hibernate.jpa.internal.util.LockOptionsHelper; import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder; import org.hibernate.service.spi.ServiceRegistryImplementor; -import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptor; import static org.hibernate.cfg.AvailableSettings.JAKARTA_LOCK_SCOPE; import static org.hibernate.cfg.AvailableSettings.JAKARTA_LOCK_TIMEOUT; @@ -147,6 +147,7 @@ public final class FastSessionServices { final boolean disallowOutOfTransactionUpdateOperations; final boolean useStreamForLobBinding; final int preferredSqlTypeCodeForBoolean; + final TimeZoneStorageStrategy defaultTimeZoneStorageStrategy; final boolean requiresMultiTenantConnectionProvider; final ConnectionProvider connectionProvider; final MultiTenantConnectionProvider multiTenantConnectionProvider; @@ -216,6 +217,7 @@ public final class FastSessionServices { this.disallowOutOfTransactionUpdateOperations = !sessionFactoryOptions.isAllowOutOfTransactionUpdateOperations(); this.useStreamForLobBinding = Environment.useStreamsForBinary() || dialect.useInputStreamToInsertBlob(); this.preferredSqlTypeCodeForBoolean = sessionFactoryOptions.getPreferredSqlTypeCodeForBoolean(); + this.defaultTimeZoneStorageStrategy = sessionFactoryOptions.getDefaultTimeZoneStorageStrategy(); this.requiresMultiTenantConnectionProvider = sf.getSettings().getMultiTenancyStrategy().requiresMultiTenantConnectionProvider(); //Some "hot" services: @@ -361,4 +363,8 @@ public final class FastSessionServices { public int getPreferredSqlTypeCodeForBoolean() { return preferredSqlTypeCodeForBoolean; } + + public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() { + return defaultTimeZoneStorageStrategy; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java b/hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java index 5de97d67cc..95440bddfc 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java @@ -12,6 +12,7 @@ import java.util.function.Consumer; import java.util.function.Function; import org.hibernate.MappingException; +import org.hibernate.TimeZoneStorageStrategy; import org.hibernate.boot.model.TypeDefinition; import org.hibernate.boot.model.TypeDefinitionRegistry; import org.hibernate.boot.model.convert.internal.ClassBasedConverterDescriptor; @@ -68,7 +69,6 @@ public class BasicValue extends SimpleValue implements JdbcTypeDescriptorIndicat private static final CoreMessageLogger log = CoreLogging.messageLogger( BasicValue.class ); private final TypeConfiguration typeConfiguration; - private final int preferredJdbcTypeCodeForBoolean; // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // incoming "configuration" values @@ -102,7 +102,6 @@ public class BasicValue extends SimpleValue implements JdbcTypeDescriptorIndicat super( buildingContext, table ); this.typeConfiguration = buildingContext.getBootstrapContext().getTypeConfiguration(); - this.preferredJdbcTypeCodeForBoolean = buildingContext.getPreferredSqlTypeCodeForBoolean(); buildingContext.getMetadataCollector().registerValueMappingResolver( this::resolve ); } @@ -600,7 +599,12 @@ public class BasicValue extends SimpleValue implements JdbcTypeDescriptorIndicat @Override public int getPreferredSqlTypeCodeForBoolean() { - return preferredJdbcTypeCodeForBoolean; + return getBuildingContext().getPreferredSqlTypeCodeForBoolean(); + } + + @Override + public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() { + return getBuildingContext().getBuildingOptions().getDefaultTimeZoneStorage(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java b/hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java index 932b83732b..b3a212a503 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/SimpleValue.java @@ -20,6 +20,7 @@ import jakarta.persistence.AttributeConverter; import org.hibernate.FetchMode; import org.hibernate.MappingException; +import org.hibernate.TimeZoneStorageStrategy; import org.hibernate.annotations.common.reflection.XProperty; import org.hibernate.boot.model.convert.internal.ClassBasedConverterDescriptor; import org.hibernate.boot.model.convert.spi.ConverterDescriptor; @@ -33,7 +34,6 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.dialect.Dialect; import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.config.spi.StandardConverters; -import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.Mapping; import org.hibernate.id.IdentifierGenerator; import org.hibernate.id.IdentityGenerator; @@ -53,6 +53,7 @@ import org.hibernate.type.descriptor.converter.AttributeConverterJdbcTypeDescrip import org.hibernate.type.descriptor.converter.AttributeConverterTypeAdapter; import org.hibernate.type.descriptor.java.BasicJavaTypeDescriptor; import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptor; +import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptorIndicators; import org.hibernate.type.descriptor.jdbc.LobTypeMappings; import org.hibernate.type.descriptor.jdbc.NationalizedTypeMappings; import org.hibernate.type.spi.TypeConfiguration; @@ -668,7 +669,17 @@ public abstract class SimpleValue implements KeyValue { // VARCHAR/CHAR final JdbcTypeDescriptor recommendedJdbcType = jpaAttributeConverter.getRelationalJavaTypeDescriptor().getRecommendedJdbcType( // todo (6.0) : handle the other JdbcRecommendedSqlTypeMappingContext methods - metadata::getTypeConfiguration + new JdbcTypeDescriptorIndicators() { + @Override + public TypeConfiguration getTypeConfiguration() { + return metadata.getTypeConfiguration(); + } + + @Override + public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() { + return buildingContext.getBuildingOptions().getDefaultTimeZoneStorage(); + } + } ); int jdbcTypeCode = recommendedJdbcType.getJdbcTypeCode(); if ( isLob() ) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/StandardBasicTypes.java b/hibernate-core/src/main/java/org/hibernate/type/StandardBasicTypes.java index bfbcd78402..b99debfb02 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/StandardBasicTypes.java +++ b/hibernate-core/src/main/java/org/hibernate/type/StandardBasicTypes.java @@ -388,7 +388,8 @@ public final class StandardBasicTypes { ); /** - * The standard Hibernate type for mapping {@link OffsetDateTime} to JDBC {@link java.sql.Types#TIMESTAMP_WITH_TIMEZONE TIMESTAMP_WITH_TIMEZONE}. + * The standard Hibernate type for mapping {@link OffsetDateTime} to JDBC {@link java.sql.Types#TIMESTAMP_WITH_TIMEZONE TIMESTAMP_WITH_TIMEZONE} + * or {@link java.sql.Types#TIMESTAMP TIMESTAMP} depending on the {@link org.hibernate.cfg.AvailableSettings#TIMEZONE_DEFAULT_STORAGE} setting. */ public static final BasicTypeReference OFFSET_DATE_TIME = new BasicTypeReference<>( "OffsetDateTime", @@ -396,18 +397,36 @@ public final class StandardBasicTypes { Types.TIMESTAMP_WITH_TIMEZONE ); + /** + * The standard Hibernate type for mapping {@link OffsetDateTime} to JDBC {@link java.sql.Types#TIMESTAMP_WITH_TIMEZONE TIMESTAMP_WITH_TIMEZONE}. + */ + public static final BasicTypeReference OFFSET_DATE_TIME_WITH_TIMEZONE = new BasicTypeReference<>( + "OffsetDateTimeWithTimezone", + OffsetDateTime.class, + Types.TIMESTAMP_WITH_TIMEZONE + ); + /** + * The standard Hibernate type for mapping {@link OffsetDateTime} to JDBC {@link java.sql.Types#TIMESTAMP TIMESTAMP}. + */ + public static final BasicTypeReference OFFSET_DATE_TIME_WITHOUT_TIMEZONE = new BasicTypeReference<>( + "OffsetDateTimeWithoutTimezone", + OffsetDateTime.class, + Types.TIMESTAMP + ); + /** * The standard Hibernate type for mapping {@link OffsetTime} to JDBC {@link java.sql.Types#TIME TIME}. */ public static final BasicTypeReference OFFSET_TIME = new BasicTypeReference<>( - "ZonedDateTime", + "OffsetTime", OffsetTime.class, // todo (6.0): why not TIME_WITH_TIMEZONE ? Types.TIME ); /** - * The standard Hibernate type for mapping {@link ZonedDateTime} to JDBC {@link java.sql.Types#TIMESTAMP_WITH_TIMEZONE TIMESTAMP_WITH_TIMEZONE}. + * The standard Hibernate type for mapping {@link ZonedDateTime} to JDBC {@link java.sql.Types#TIMESTAMP_WITH_TIMEZONE TIMESTAMP_WITH_TIMEZONE} + * or {@link java.sql.Types#TIMESTAMP TIMESTAMP} depending on the {@link org.hibernate.cfg.AvailableSettings#TIMEZONE_DEFAULT_STORAGE} setting. */ public static final BasicTypeReference ZONED_DATE_TIME = new BasicTypeReference<>( "ZonedDateTime", @@ -415,6 +434,24 @@ public final class StandardBasicTypes { Types.TIMESTAMP_WITH_TIMEZONE ); + /** + * The standard Hibernate type for mapping {@link ZonedDateTime} to JDBC {@link java.sql.Types#TIMESTAMP_WITH_TIMEZONE TIMESTAMP_WITH_TIMEZONE}. + */ + public static final BasicTypeReference ZONED_DATE_TIME_WITH_TIMEZONE = new BasicTypeReference<>( + "ZonedDateTimeWithTimezone", + ZonedDateTime.class, + Types.TIMESTAMP_WITH_TIMEZONE + ); + + /** + * The standard Hibernate type for mapping {@link ZonedDateTime} to JDBC {@link java.sql.Types#TIMESTAMP TIMESTAMP}. + */ + public static final BasicTypeReference ZONED_DATE_TIME_WITHOUT_TIMEZONE = new BasicTypeReference<>( + "ZonedDateTimeWithoutTimezone", + ZonedDateTime.class, + Types.TIMESTAMP + ); + /** * The standard Hibernate type for mapping {@link Instant} to JDBC * {@link java.sql.Types#TIMESTAMP TIMESTAMP}. diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaTypeDescriptor.java index e6d7ba560c..13e914518b 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarDateJavaTypeDescriptor.java @@ -6,6 +6,7 @@ */ package org.hibernate.type.descriptor.java; +import java.sql.Types; import java.util.Calendar; import java.util.Comparator; import java.util.Date; @@ -40,7 +41,7 @@ public class CalendarDateJavaTypeDescriptor extends AbstractTemporalJavaTypeDesc @Override public JdbcTypeDescriptor getRecommendedJdbcType(JdbcTypeDescriptorIndicators context) { - return DateJdbcTypeDescriptor.INSTANCE; + return context.getTypeConfiguration().getJdbcTypeDescriptorRegistry().getDescriptor( Types.DATE ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaTypeDescriptor.java index ab4e511c88..0b098587a4 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarJavaTypeDescriptor.java @@ -6,6 +6,7 @@ */ package org.hibernate.type.descriptor.java; +import java.sql.Types; import java.util.Calendar; import java.util.Comparator; import java.util.GregorianCalendar; @@ -49,7 +50,7 @@ public class CalendarJavaTypeDescriptor extends AbstractTemporalJavaTypeDescript @Override public JdbcTypeDescriptor getRecommendedJdbcType(JdbcTypeDescriptorIndicators context) { - return TimestampJdbcTypeDescriptor.INSTANCE; + return context.getTypeConfiguration().getJdbcTypeDescriptorRegistry().getDescriptor( Types.TIMESTAMP ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaTypeDescriptor.java index 213df3d3b9..996df8bdeb 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/CalendarTimeJavaTypeDescriptor.java @@ -6,6 +6,7 @@ */ package org.hibernate.type.descriptor.java; +import java.sql.Types; import java.util.Calendar; import java.util.Comparator; import java.util.Date; @@ -40,7 +41,7 @@ public class CalendarTimeJavaTypeDescriptor extends AbstractTemporalJavaTypeDesc @Override public JdbcTypeDescriptor getRecommendedJdbcType(JdbcTypeDescriptorIndicators context) { - return TimeJdbcTypeDescriptor.INSTANCE; + return context.getTypeConfiguration().getJdbcTypeDescriptorRegistry().getDescriptor( Types.TIME ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaTypeDescriptor.java index cf499ce2b1..adf24dc02a 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaTypeDescriptor.java @@ -7,6 +7,7 @@ package org.hibernate.type.descriptor.java; import java.sql.Timestamp; +import java.sql.Types; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -57,7 +58,7 @@ public class DateJavaTypeDescriptor extends AbstractTemporalJavaTypeDescriptor) prop.getType(); assertSame( StringJavaTypeDescriptor.INSTANCE, type.getJavaTypeDescriptor() ); if ( dialect.getNationalizationSupport() != NationalizationSupport.EXPLICIT ) { - // See issue HHH-10693 - if ( dialect instanceof SybaseDialect ) { - assertSame( ClobJdbcTypeDescriptor.CLOB_BINDING, type.getJdbcTypeDescriptor() ); - } - else { - assertSame( ClobJdbcTypeDescriptor.DEFAULT, type.getJdbcTypeDescriptor() ); - } + assertSame( jdbcTypeRegistry.getDescriptor( Types.CLOB ), type.getJdbcTypeDescriptor() ); } else { - assertSame( NClobJdbcTypeDescriptor.DEFAULT, type.getJdbcTypeDescriptor() ); + assertSame( jdbcTypeRegistry.getDescriptor( Types.NCLOB ), type.getJdbcTypeDescriptor() ); } prop = pc.getProperty( "nclobAtt" ); type = (BasicType) prop.getType(); assertSame( NClobJavaTypeDescriptor.INSTANCE, type.getJavaTypeDescriptor() ); if ( dialect.getNationalizationSupport() != NationalizationSupport.EXPLICIT ) { - // See issue HHH-10693 - if ( dialect instanceof SybaseDialect ) { - assertSame( ClobJdbcTypeDescriptor.CLOB_BINDING, type.getJdbcTypeDescriptor() ); - } - else { - assertSame( ClobJdbcTypeDescriptor.DEFAULT, type.getJdbcTypeDescriptor() ); - } + assertSame( jdbcTypeRegistry.getDescriptor( Types.CLOB ), type.getJdbcTypeDescriptor() ); } else { - assertSame( NClobJdbcTypeDescriptor.DEFAULT, type.getJdbcTypeDescriptor() ); + assertSame( jdbcTypeRegistry.getDescriptor( Types.NCLOB ), type.getJdbcTypeDescriptor() ); } prop = pc.getProperty( "nlongvarcharcharAtt" ); - { - final BasicValue.Resolution resolution = ( (BasicValue) prop.getValue() ).resolve(); - - final int jdbcTypeExpected; - if ( dialect.getNationalizationSupport() != NationalizationSupport.EXPLICIT ) { - jdbcTypeExpected = Types.CLOB; - } - else { - jdbcTypeExpected = Types.NCLOB; - } - Assertions.assertThat( resolution.getJdbcTypeDescriptor().getJdbcTypeCode() ).isEqualTo( jdbcTypeExpected ); + type = (BasicType) prop.getType(); + assertSame( StringJavaTypeDescriptor.INSTANCE, type.getJavaTypeDescriptor() ); + if ( dialect.getNationalizationSupport() != NationalizationSupport.EXPLICIT ) { + assertSame( jdbcTypeRegistry.getDescriptor( Types.CLOB ), type.getJdbcTypeDescriptor() ); + } + else { + assertSame( jdbcTypeRegistry.getDescriptor( Types.NCLOB ), type.getJdbcTypeDescriptor() ); } prop = pc.getProperty( "ncharArrAtt" ); type = (BasicType) prop.getType(); assertSame( CharacterArrayJavaTypeDescriptor.INSTANCE, type.getJavaTypeDescriptor() ); if ( dialect.getNationalizationSupport() != NationalizationSupport.EXPLICIT ) { - // See issue HHH-10693 - assertSame( VarcharJdbcTypeDescriptor.INSTANCE, type.getJdbcTypeDescriptor() ); + assertSame( jdbcTypeRegistry.getDescriptor( Types.VARCHAR ), type.getJdbcTypeDescriptor() ); } else { - assertSame( NVarcharJdbcTypeDescriptor.INSTANCE, type.getJdbcTypeDescriptor() ); + assertSame( jdbcTypeRegistry.getDescriptor( Types.NVARCHAR ), type.getJdbcTypeDescriptor() ); } prop = pc.getProperty( "ncharacterAtt" ); type = (BasicType) prop.getType(); assertSame( CharacterJavaTypeDescriptor.INSTANCE, type.getJavaTypeDescriptor() ); if ( dialect.getNationalizationSupport() != NationalizationSupport.EXPLICIT ) { - // See issue HHH-10693 - assertSame( CharJdbcTypeDescriptor.INSTANCE, type.getJdbcTypeDescriptor() ); + assertSame( jdbcTypeRegistry.getDescriptor( Types.CHAR ), type.getJdbcTypeDescriptor() ); } else { - assertSame( NCharJdbcTypeDescriptor.INSTANCE, type.getJdbcTypeDescriptor() ); + assertSame( jdbcTypeRegistry.getDescriptor( Types.NCHAR ), type.getJdbcTypeDescriptor() ); } } finally { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/nationalized/UseNationalizedCharDataSettingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/nationalized/UseNationalizedCharDataSettingTest.java index d526bef3af..71ac8d18c9 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/nationalized/UseNationalizedCharDataSettingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/nationalized/UseNationalizedCharDataSettingTest.java @@ -6,6 +6,8 @@ */ package org.hibernate.orm.test.nationalized; +import java.sql.Types; + import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; @@ -27,10 +29,12 @@ import org.hibernate.type.descriptor.jdbc.CharJdbcTypeDescriptor; import org.hibernate.type.descriptor.jdbc.NCharJdbcTypeDescriptor; import org.hibernate.type.descriptor.jdbc.NVarcharJdbcTypeDescriptor; import org.hibernate.type.descriptor.jdbc.VarcharJdbcTypeDescriptor; +import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeDescriptorRegistry; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseUnitTestCase; import org.junit.Test; +import org.junit.jupiter.api.Assertions; import static org.junit.Assert.assertSame; @@ -53,18 +57,19 @@ public class UseNationalizedCharDataSettingTest extends BaseUnitTestCase { ms.addAnnotatedClass( NationalizedBySettingEntity.class ); final Metadata metadata = ms.buildMetadata(); + final JdbcTypeDescriptorRegistry jdbcTypeRegistry = metadata.getDatabase() + .getTypeConfiguration() + .getJdbcTypeDescriptorRegistry(); final PersistentClass pc = metadata.getEntityBinding( NationalizedBySettingEntity.class.getName() ); final Property nameAttribute = pc.getProperty( "name" ); final BasicType type = (BasicType) nameAttribute.getType(); final Dialect dialect = metadata.getDatabase().getDialect(); + assertSame( StringJavaTypeDescriptor.INSTANCE, type.getJavaTypeDescriptor() ); if ( dialect.getNationalizationSupport() != NationalizationSupport.EXPLICIT ) { - // See issue HHH-10693 - assertSame( StringJavaTypeDescriptor.INSTANCE, type.getJavaTypeDescriptor() ); - assertSame( VarcharJdbcTypeDescriptor.INSTANCE, type.getJdbcTypeDescriptor() ); + Assertions.assertSame( jdbcTypeRegistry.getDescriptor( Types.VARCHAR ), type.getJdbcTypeDescriptor() ); } else { - assertSame( StringJavaTypeDescriptor.INSTANCE, type.getJavaTypeDescriptor() ); - assertSame( NVarcharJdbcTypeDescriptor.INSTANCE, type.getJdbcTypeDescriptor() ); + Assertions.assertSame( jdbcTypeRegistry.getDescriptor( Types.NVARCHAR ), type.getJdbcTypeDescriptor() ); } } @@ -85,17 +90,19 @@ public class UseNationalizedCharDataSettingTest extends BaseUnitTestCase { ms.addAnnotatedClass( NationalizedBySettingEntity.class ); final Metadata metadata = ms.buildMetadata(); + final JdbcTypeDescriptorRegistry jdbcTypeRegistry = metadata.getDatabase() + .getTypeConfiguration() + .getJdbcTypeDescriptorRegistry(); final PersistentClass pc = metadata.getEntityBinding( NationalizedBySettingEntity.class.getName() ); final Property nameAttribute = pc.getProperty( "flag" ); final BasicType type = (BasicType) nameAttribute.getType(); final Dialect dialect = metadata.getDatabase().getDialect(); + assertSame( CharacterJavaTypeDescriptor.INSTANCE, type.getJavaTypeDescriptor() ); if ( dialect.getNationalizationSupport() != NationalizationSupport.EXPLICIT ) { - assertSame( CharacterJavaTypeDescriptor.INSTANCE, type.getJavaTypeDescriptor() ); - assertSame( CharJdbcTypeDescriptor.INSTANCE, type.getJdbcTypeDescriptor() ); + Assertions.assertSame( jdbcTypeRegistry.getDescriptor( Types.CHAR ), type.getJdbcTypeDescriptor() ); } else { - assertSame( CharacterJavaTypeDescriptor.INSTANCE, type.getJavaTypeDescriptor() ); - assertSame( NCharJdbcTypeDescriptor.INSTANCE, type.getJdbcTypeDescriptor() ); + Assertions.assertSame( jdbcTypeRegistry.getDescriptor( Types.NCHAR ), type.getJdbcTypeDescriptor() ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/AbstractJavaTimeTypeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/AbstractJavaTimeTypeTest.java similarity index 93% rename from hibernate-core/src/test/java/org/hibernate/test/type/AbstractJavaTimeTypeTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/type/AbstractJavaTimeTypeTest.java index 075768d041..b92f96605a 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/AbstractJavaTimeTypeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/AbstractJavaTimeTypeTest.java @@ -4,7 +4,7 @@ * 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.test.type; +package org.hibernate.orm.test.type; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -20,6 +20,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.function.Consumer; +import java.util.function.Predicate; import org.hibernate.boot.model.TypeContributions; import org.hibernate.cfg.AvailableSettings; @@ -27,7 +28,6 @@ import org.hibernate.cfg.Configuration; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.H2Dialect; import org.hibernate.service.ServiceRegistry; -import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptor; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; @@ -47,7 +47,7 @@ import static org.junit.Assert.assertEquals; * @param The entity type used in tests. */ @RunWith(CustomParameterized.class) -abstract class AbstractJavaTimeTypeTest extends BaseCoreFunctionalTestCase { +public abstract class AbstractJavaTimeTypeTest extends BaseCoreFunctionalTestCase { private static Dialect determineDialect() { try { @@ -287,6 +287,13 @@ abstract class AbstractJavaTimeTypeTest extends BaseCoreFunctionalTestCase return thisAsS(); } + public S skippedForDialects(Predicate skipPredicate, Consumer skippedIfDialectMatchesClasses) { + if ( !skipPredicate.test( dialect ) ) { + skippedIfDialectMatchesClasses.accept( thisAsS() ); + } + return thisAsS(); + } + public S withForcedJdbcTimezone(String zoneIdString, Consumer contributor) { ZoneId zoneId = ZoneId.of( zoneIdString ); this.forcedJdbcTimeZone = zoneId; @@ -301,7 +308,7 @@ abstract class AbstractJavaTimeTypeTest extends BaseCoreFunctionalTestCase @SafeVarargs public final S alsoTestRemappingsWithH2(Class ... dialectClasses) { - if ( dialect instanceof H2Dialect ) { + if ( dialect instanceof H2Dialect && !( (H2Dialect) dialect ).hasDstBug() ) { // Only test remappings with H2 Collections.addAll( remappingDialectClasses, dialectClasses ); } @@ -395,11 +402,12 @@ abstract class AbstractJavaTimeTypeTest extends BaseCoreFunctionalTestCase protected static class AbstractRemappingH2Dialect extends H2Dialect { private final int overriddenSqlTypeCode; - private final JdbcTypeDescriptor overriddenJdbcTypeDescriptor; + private final int overridingSqlTypeCode; - public AbstractRemappingH2Dialect(int overriddenSqlTypeCode, JdbcTypeDescriptor overriddenJdbcTypeDescriptor) { + public AbstractRemappingH2Dialect(int overriddenSqlTypeCode, int overridingSqlTypeCode) { + super( getDialect().getVersion() ); this.overriddenSqlTypeCode = overriddenSqlTypeCode; - this.overriddenJdbcTypeDescriptor = overriddenJdbcTypeDescriptor; + this.overridingSqlTypeCode = overridingSqlTypeCode; } @Override @@ -408,7 +416,9 @@ abstract class AbstractJavaTimeTypeTest extends BaseCoreFunctionalTestCase typeContributions.getTypeConfiguration().getJdbcTypeDescriptorRegistry().addDescriptor( overriddenSqlTypeCode, - overriddenJdbcTypeDescriptor + typeContributions.getTypeConfiguration().getJdbcTypeDescriptorRegistry().getDescriptor( + overridingSqlTypeCode + ) ); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/BigDecimalTypeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/BigDecimalTypeTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/test/type/BigDecimalTypeTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/type/BigDecimalTypeTest.java index 9c6dac54b1..198ff92932 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/BigDecimalTypeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/BigDecimalTypeTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.type; +package org.hibernate.orm.test.type; import org.hibernate.Session; import org.hibernate.query.Query; diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/BinaryTypeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/BinaryTypeTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/test/type/BinaryTypeTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/type/BinaryTypeTest.java index 3b4107dd38..8f9ea93edf 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/BinaryTypeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/BinaryTypeTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.type; +package org.hibernate.orm.test.type; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/InstantTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/InstantTest.java similarity index 88% rename from hibernate-core/src/test/java/org/hibernate/test/type/InstantTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/type/InstantTest.java index edb91a849d..5d16c7bf11 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/InstantTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/InstantTest.java @@ -4,7 +4,7 @@ * 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.test.type; +package org.hibernate.orm.test.type; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -22,8 +22,10 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; +import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.MySQLDialect; +import org.hibernate.dialect.SybaseASEDialect; import org.hibernate.dialect.SybaseDialect; import org.junit.runners.Parameterized; @@ -61,21 +63,26 @@ public class InstantTest extends AbstractJavaTimeTypeTest dialect instanceof MySQLDialect || dialect instanceof MariaDBDialect + || dialect instanceof SybaseDialect + || dialect instanceof H2Dialect && ( (H2Dialect) dialect ).hasDstBug(), b -> b .add( 1600, 1, 1, 0, 0, 0, 0, ZONE_AMSTERDAM ) + // Affected by HHH-13266 (JDK-8061577) + .add( 1892, 1, 1, 0, 0, 0, 0, ZONE_OSLO ) ) // HHH-13379: DST end (where Timestamp becomes ambiguous, see JDK-4312621) // => This used to work correctly in 5.4.1.Final and earlier - .add( 2018, 10, 28, 1, 0, 0, 0, ZONE_PARIS ) - .add( 2018, 3, 31, 14, 0, 0, 0, ZONE_AUCKLAND ) + .skippedForDialects( + dialect -> dialect instanceof H2Dialect && ( (H2Dialect) dialect ).hasDstBug(), + b -> b.add( 2018, 10, 28, 1, 0, 0, 0, ZONE_PARIS ) + .add( 2018, 3, 31, 14, 0, 0, 0, ZONE_AUCKLAND ) + ) // => This has never worked correctly, unless the JDBC timezone was set to UTC .withForcedJdbcTimezone( "UTC", b -> b .add( 2018, 10, 28, 0, 0, 0, 0, ZONE_PARIS ) @@ -83,8 +90,12 @@ public class InstantTest extends AbstractJavaTimeTypeTest Also test DST start, just in case .add( 2018, 3, 25, 1, 0, 0, 0, ZONE_PARIS ) - .add( 2018, 3, 25, 2, 0, 0, 0, ZONE_PARIS ) - .add( 2018, 9, 30, 2, 0, 0, 0, ZONE_AUCKLAND ) + .skippedForDialects( + // No idea what Sybase is doing here exactly + dialect -> dialect instanceof SybaseASEDialect, + b -> b.add( 2018, 3, 25, 2, 0, 0, 0, ZONE_PARIS ) + .add( 2018, 9, 30, 2, 0, 0, 0, ZONE_AUCKLAND ) + ) .add( 2018, 9, 30, 3, 0, 0, 0, ZONE_AUCKLAND ) // => Also test dates around 1905-01-01, because the code behaves differently before and after 1905 .add( 1904, 12, 31, 22, 59, 59, 999_999_999, ZONE_PARIS ) diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/Java8DateTimeTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/Java8DateTimeTests.java similarity index 99% rename from hibernate-core/src/test/java/org/hibernate/test/type/Java8DateTimeTests.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/type/Java8DateTimeTests.java index d9f8d54cc5..938720edf2 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/Java8DateTimeTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/Java8DateTimeTests.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.type; +package org.hibernate.orm.test.type; import java.time.Duration; import java.time.Instant; diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/LobUnfetchedPropertyTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/LobUnfetchedPropertyTest.java similarity index 99% rename from hibernate-core/src/test/java/org/hibernate/test/type/LobUnfetchedPropertyTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/type/LobUnfetchedPropertyTest.java index ec9f85b1b6..1ae945bb9d 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/LobUnfetchedPropertyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/LobUnfetchedPropertyTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.type; +package org.hibernate.orm.test.type; import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; import static org.junit.Assert.assertFalse; diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/LocalDateTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/LocalDateTest.java similarity index 89% rename from hibernate-core/src/test/java/org/hibernate/test/type/LocalDateTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/type/LocalDateTest.java index 55f6457303..1f8427dc8d 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/LocalDateTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/LocalDateTest.java @@ -4,7 +4,7 @@ * 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.test.type; +package org.hibernate.orm.test.type; import java.sql.Date; import java.sql.PreparedStatement; @@ -22,10 +22,11 @@ import jakarta.persistence.Entity; import jakarta.persistence.Id; import org.hibernate.dialect.AbstractHANADialect; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.dialect.HSQLDialect; import org.hibernate.dialect.MariaDBDialect; -import org.hibernate.dialect.MySQL5Dialect; import org.hibernate.dialect.MySQLDialect; -import org.hibernate.type.descriptor.jdbc.TimestampJdbcTypeDescriptor; +import org.hibernate.dialect.SybaseASEDialect; import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.TestForIssue; @@ -37,11 +38,13 @@ import org.junit.runners.Parameterized; @TestForIssue(jiraKey = "HHH-10371") @SkipForDialect(value = AbstractHANADialect.class, comment = "HANA systematically returns the wrong date when the JVM default timezone is not UTC") -@SkipForDialect(value = MySQL5Dialect.class, +@SkipForDialect(value = MySQLDialect.class, comment = "HHH-13582: MySQL ConnectorJ 8.x returns the wrong date" + " when the JVM default timezone is different from the server timezone:" + " https://bugs.mysql.com/bug.php?id=91112" ) +@SkipForDialect(value = H2Dialect.class, comment = "H2 1.4.200 DST bug. See org.hibernate.dialect.H2Dialect.hasDstBug") +@SkipForDialect(value = HSQLDialect.class, comment = "HSQL has problems with DST edges") public class LocalDateTest extends AbstractJavaTimeTypeTest { private static class ParametersBuilder extends AbstractParametersBuilder { @@ -70,7 +73,11 @@ public class LocalDateTest extends AbstractJavaTimeTypeTest dialect instanceof SybaseASEDialect, + b -> b.add( 1600, 1, 1, ZONE_AMSTERDAM ) ) // HHH-13379: DST end (where Timestamp becomes ambiguous, see JDK-4312621) // It doesn't seem that any date at midnight can be affected by HHH-13379, but we add some tests just in case @@ -167,7 +174,7 @@ public class LocalDateTest extends AbstractJavaTimeTypeTest dialect instanceof MySQLDialect || dialect instanceof MariaDBDialect + || dialect instanceof H2Dialect && ( (H2Dialect) dialect ).hasDstBug(), + b -> b + // Affected by HHH-13266 (JDK-8061577) + .add( 1892, 1, 1, 0, 0, 0, 0, ZONE_OSLO ) + ) .skippedForDialects( // MySQL/Mariadb/Sybase cannot store dates in 1600 in a timestamp. - Arrays.asList( MySQLDialect.class, MariaDBDialect.class, SybaseDialect.class ), + dialect -> dialect instanceof MySQLDialect || dialect instanceof MariaDBDialect || dialect instanceof SybaseDialect + || dialect instanceof H2Dialect && ( (H2Dialect) dialect ).hasDstBug(), b -> b .add( 1600, 1, 1, 0, 0, 0, 0, ZONE_AMSTERDAM ) ) // HHH-13379: DST end (where Timestamp becomes ambiguous, see JDK-4312621) // It doesn't seem that any LocalDateTime can be affected by HHH-13379, but we add some tests just in case .add( 2018, 10, 28, 1, 0, 0, 0, ZONE_PARIS ) - .add( 2018, 10, 28, 2, 0, 0, 0, ZONE_PARIS ) + .skippedForDialects( + dialect -> dialect instanceof H2Dialect && ( (H2Dialect) dialect ).hasDstBug(), + b -> b + .add( 2018, 10, 28, 2, 0, 0, 0, ZONE_PARIS ) + ) .add( 2018, 10, 28, 3, 0, 0, 0, ZONE_PARIS ) .add( 2018, 10, 28, 4, 0, 0, 0, ZONE_PARIS ) .add( 2018, 4, 1, 1, 0, 0, 0, ZONE_AUCKLAND ) - .add( 2018, 4, 1, 2, 0, 0, 0, ZONE_AUCKLAND ) + .skippedForDialects( + dialect -> dialect instanceof H2Dialect && ( (H2Dialect) dialect ).hasDstBug(), + b -> b + .add( 2018, 4, 1, 2, 0, 0, 0, ZONE_AUCKLAND ) + ) .add( 2018, 4, 1, 3, 0, 0, 0, ZONE_AUCKLAND ) .add( 2018, 4, 1, 4, 0, 0, 0, ZONE_AUCKLAND ) // => Also test DST start diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/LocalTimeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/LocalTimeTest.java similarity index 95% rename from hibernate-core/src/test/java/org/hibernate/test/type/LocalTimeTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/type/LocalTimeTest.java index 221214bbea..9243d1012c 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/LocalTimeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/LocalTimeTest.java @@ -4,7 +4,7 @@ * 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.test.type; +package org.hibernate.orm.test.type; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -23,10 +23,10 @@ import jakarta.persistence.Entity; import jakarta.persistence.Id; import org.hibernate.dialect.AbstractHANADialect; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.dialect.HSQLDialect; import org.hibernate.dialect.MariaDBDialect; -import org.hibernate.dialect.MySQL5Dialect; import org.hibernate.dialect.MySQLDialect; -import org.hibernate.type.descriptor.jdbc.TimestampJdbcTypeDescriptor; import org.hibernate.testing.SkipForDialect; import org.junit.Test; @@ -35,6 +35,7 @@ import org.junit.runners.Parameterized; /** * Tests for storage of LocalTime properties. */ +@SkipForDialect(value = H2Dialect.class, comment = "H2 1.4.200 DST bug. See org.hibernate.dialect.H2Dialect.hasDstBug") public class LocalTimeTest extends AbstractJavaTimeTypeTest { private static class ParametersBuilder extends AbstractParametersBuilder { @@ -200,12 +201,13 @@ public class LocalTimeTest extends AbstractJavaTimeTypeTest. */ -package org.hibernate.test.type; +package org.hibernate.orm.test.type; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -22,11 +22,11 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; +import org.hibernate.dialect.H2Dialect; import org.hibernate.query.Query; import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.SybaseDialect; -import org.hibernate.type.OffsetDateTimeType; import org.hibernate.type.StandardBasicTypes; import org.hibernate.testing.TestForIssue; @@ -88,22 +88,32 @@ public class OffsetDateTimeTest extends AbstractJavaTimeTypeTest dialect instanceof MySQLDialect || dialect instanceof MariaDBDialect + || dialect instanceof H2Dialect && ( (H2Dialect) dialect ).hasDstBug(), + b -> b + // Affected by HHH-13266 (JDK-8061577) + .add( 1892, 1, 1, 0, 0, 0, 0, "+00:00", ZONE_OSLO ) + ) .skippedForDialects( // MySQL/Mariadb/Sybase cannot store dates in 1600 in a timestamp. - Arrays.asList( MySQLDialect.class, MariaDBDialect.class, SybaseDialect.class ), + dialect -> dialect instanceof MySQLDialect || dialect instanceof MariaDBDialect || dialect instanceof SybaseDialect + || dialect instanceof H2Dialect && ( (H2Dialect) dialect ).hasDstBug(), b -> b .add( 1600, 1, 1, 0, 0, 0, 0, "+00:19:32", ZONE_AMSTERDAM ) ) // HHH-13379: DST end (where Timestamp becomes ambiguous, see JDK-4312621) // => This used to work correctly in 5.4.1.Final and earlier - .add( 2018, 10, 28, 2, 0, 0, 0, "+01:00", ZONE_PARIS ) - .add( 2018, 4, 1, 2, 0, 0, 0, "+12:00", ZONE_AUCKLAND ) + .skippedForDialects( + dialect -> dialect instanceof H2Dialect && ( (H2Dialect) dialect ).hasDstBug(), + b -> b.add( 2018, 10, 28, 2, 0, 0, 0, "+01:00", ZONE_PARIS ) + .add( 2018, 4, 1, 2, 0, 0, 0, "+12:00", ZONE_AUCKLAND ) + ) // => This has never worked correctly, unless the JDBC timezone was set to UTC .withForcedJdbcTimezone( "UTC", b -> b .add( 2018, 10, 28, 2, 0, 0, 0, "+02:00", ZONE_PARIS ) diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/OffsetTimeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/OffsetTimeTest.java similarity index 96% rename from hibernate-core/src/test/java/org/hibernate/test/type/OffsetTimeTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/type/OffsetTimeTest.java index 617cb92adc..4f17cf8cb4 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/OffsetTimeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/OffsetTimeTest.java @@ -4,7 +4,7 @@ * 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.test.type; +package org.hibernate.orm.test.type; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -25,11 +25,9 @@ import jakarta.persistence.Entity; import jakarta.persistence.Id; import org.hibernate.dialect.AbstractHANADialect; +import org.hibernate.dialect.HSQLDialect; import org.hibernate.dialect.MariaDBDialect; -import org.hibernate.dialect.MySQL5Dialect; import org.hibernate.dialect.MySQLDialect; -import org.hibernate.type.descriptor.jdbc.BigIntJdbcTypeDescriptor; -import org.hibernate.type.descriptor.jdbc.TimestampJdbcTypeDescriptor; import org.hibernate.testing.SkipForDialect; import org.junit.Test; @@ -229,12 +227,13 @@ public class OffsetTimeTest extends AbstractJavaTimeTypeTest. */ -package org.hibernate.test.type; +package org.hibernate.orm.test.type; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -17,35 +17,34 @@ import org.hibernate.boot.MetadataSources; import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.boot.spi.MetadataImplementor; -import org.hibernate.dialect.SQLServer2008Dialect; +import org.hibernate.dialect.SQLServerDialect; import org.hibernate.tool.hbm2ddl.SchemaExport; import org.hibernate.tool.schema.TargetType; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.hibernate.testing.orm.junit.RequiresDialect; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; /** * @author Andrea Boriero */ @TestForIssue(jiraKey = "HHH-10529") -@RequiresDialect(value = SQLServer2008Dialect.class) -public class SQLServer2008NVarCharTypeTest extends BaseUnitTestCase { +@RequiresDialect(value = SQLServerDialect.class, version = 10) +public class SQLServer2008NVarCharTypeTest { private StandardServiceRegistry ssr; private MetadataImplementor metadata; private SchemaExport schemaExport; - @Before + @BeforeEach public void setUp() { ssr = new StandardServiceRegistryBuilder().build(); schemaExport = createSchemaExport( new Class[] {MyEntity.class} ); } - @After + @AfterEach public void tearDown() { schemaExport.drop( EnumSet.of( TargetType.DATABASE ), metadata ); StandardServiceRegistryBuilder.destroy( ssr ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/SmallIntToShortClassMappingTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/SmallIntToShortClassMappingTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/test/type/SmallIntToShortClassMappingTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/type/SmallIntToShortClassMappingTest.java index 14d211500d..cf00787d23 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/SmallIntToShortClassMappingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/SmallIntToShortClassMappingTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.type; +package org.hibernate.orm.test.type; import jakarta.persistence.AttributeConverter; import jakarta.persistence.Column; diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/StandardBasicTypeTemplateTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/StandardBasicTypeTemplateTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/test/type/StandardBasicTypeTemplateTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/type/StandardBasicTypeTemplateTest.java index b97172fe3c..c0d05e12f8 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/StandardBasicTypeTemplateTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/StandardBasicTypeTemplateTest.java @@ -4,7 +4,7 @@ * 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.test.type; +package org.hibernate.orm.test.type; import java.net.URL; diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/TimeAndTimestampTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/TimeAndTimestampTest.java similarity index 93% rename from hibernate-core/src/test/java/org/hibernate/test/type/TimeAndTimestampTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/type/TimeAndTimestampTest.java index 5a95470e2d..2164d1a950 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/TimeAndTimestampTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/TimeAndTimestampTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.type; +package org.hibernate.orm.test.type; import java.sql.Time; import java.sql.Timestamp; @@ -50,14 +50,14 @@ public class TimeAndTimestampTest extends BaseNonConfigCoreFunctionalTestCase { Event event = new Event(); event.id = 1L; event.timeValue = new Time( 1000 ); - event.timestampValue = new Timestamp( 45678 ); + event.timestampValue = new Timestamp( 45677 ); session.persist( event ); } ); doInHibernate( this::sessionFactory, session -> { Event event = session.find( Event.class, 1L ); assertEquals(1000, event.timeValue.getTime() % TimeUnit.DAYS.toMillis( 1 )); - assertEquals(45678, event.timestampValue.getTime() % TimeUnit.DAYS.toMillis( 1 )); + assertEquals(45677, event.timestampValue.getTime() % TimeUnit.DAYS.toMillis( 1 )); } ); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/TypeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/TypeTest.java similarity index 99% rename from hibernate-core/src/test/java/org/hibernate/test/type/TypeTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/type/TypeTest.java index 29ecd406dc..81cab1ec23 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/TypeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/TypeTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.type; +package org.hibernate.orm.test.type; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; diff --git a/hibernate-core/src/test/java/org/hibernate/test/type/ZonedDateTimeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/ZonedDateTimeTest.java similarity index 92% rename from hibernate-core/src/test/java/org/hibernate/test/type/ZonedDateTimeTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/type/ZonedDateTimeTest.java index 203556c7d3..9954758c75 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/type/ZonedDateTimeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/ZonedDateTimeTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.type; +package org.hibernate.orm.test.type; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -22,12 +22,12 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; +import org.hibernate.dialect.H2Dialect; import org.hibernate.query.Query; import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.MySQLDialect; import org.hibernate.dialect.SybaseDialect; import org.hibernate.type.StandardBasicTypes; -import org.hibernate.type.ZonedDateTimeType; import org.hibernate.testing.TestForIssue; import org.junit.Test; @@ -96,26 +96,36 @@ public class ZonedDateTimeTest extends AbstractJavaTimeTypeTest dialect instanceof MySQLDialect || dialect instanceof MariaDBDialect + || dialect instanceof H2Dialect && ( (H2Dialect) dialect ).hasDstBug(), + b -> b + // Affected by HHH-13266 (JDK-8061577) + .add( 1892, 1, 1, 0, 0, 0, 0, "GMT+00:00", ZONE_OSLO ) + .add( 1892, 1, 1, 0, 0, 0, 0, "Europe/Oslo", ZONE_OSLO ) + ) .skippedForDialects( // MySQL/Mariadb/Sybase cannot store dates in 1600 in a timestamp. - Arrays.asList( MySQLDialect.class, MariaDBDialect.class, SybaseDialect.class ), + dialect -> dialect instanceof MySQLDialect || dialect instanceof MariaDBDialect || dialect instanceof SybaseDialect + || dialect instanceof H2Dialect && ( (H2Dialect) dialect ).hasDstBug(), b -> b .add( 1600, 1, 1, 0, 0, 0, 0, "GMT+00:19:32", ZONE_AMSTERDAM ) .add( 1600, 1, 1, 0, 0, 0, 0, "Europe/Amsterdam", ZONE_AMSTERDAM ) ) // HHH-13379: DST end (where Timestamp becomes ambiguous, see JDK-4312621) // => This used to work correctly in 5.4.1.Final and earlier - .add( 2018, 10, 28, 2, 0, 0, 0, "+01:00", ZONE_PARIS ) - .add( 2018, 4, 1, 2, 0, 0, 0, "+12:00", ZONE_AUCKLAND ) + .skippedForDialects( + dialect -> dialect instanceof H2Dialect && ( (H2Dialect) dialect ).hasDstBug(), + b -> b.add( 2018, 10, 28, 2, 0, 0, 0, "+01:00", ZONE_PARIS ) + .add( 2018, 4, 1, 2, 0, 0, 0, "+12:00", ZONE_AUCKLAND ) + ) // => This has never worked correctly, unless the JDBC timezone was set to UTC .withForcedJdbcTimezone( "UTC", b -> b .add( 2018, 10, 28, 2, 0, 0, 0, "+02:00", ZONE_PARIS ) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/typeoverride/TypeOverrideTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/typeoverride/TypeOverrideTest.java index caefdb3aa4..59c7b57dda 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/typeoverride/TypeOverrideTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/typeoverride/TypeOverrideTest.java @@ -50,33 +50,34 @@ public class TypeOverrideTest extends BaseSessionFactoryFunctionalTest { @Test public void testStandardBasicSqlTypeDescriptor() { + final Dialect dialect = getMetadata().getDatabase().getDialect(); final JdbcTypeDescriptorRegistry jdbcTypeRegistry = getMetadata().getTypeConfiguration() .getJdbcTypeDescriptorRegistry(); // no override assertSame( IntegerJdbcTypeDescriptor.INSTANCE, jdbcTypeRegistry.getDescriptor( Types.INTEGER ) ); // A few dialects explicitly override BlobTypeDescriptor.DEFAULT - if ( CockroachDialect.class.isInstance( getDialect() ) ) { + if ( CockroachDialect.class.isInstance( dialect ) ) { assertSame( VarbinaryJdbcTypeDescriptor.INSTANCE, jdbcTypeRegistry.getDescriptor( Types.BLOB ) ); } - else if ( PostgreSQLDialect.class.isInstance( getDialect() ) ) { + else if ( PostgreSQLDialect.class.isInstance( dialect ) ) { assertSame( BlobJdbcTypeDescriptor.BLOB_BINDING, jdbcTypeRegistry.getDescriptor( Types.BLOB ) ); } - else if ( SybaseDialect.class.isInstance( getDialect() ) ) { + else if ( SybaseDialect.class.isInstance( dialect ) ) { assertSame( BlobJdbcTypeDescriptor.PRIMITIVE_ARRAY_BINDING, jdbcTypeRegistry.getDescriptor( Types.BLOB ) ); } - else if ( AbstractHANADialect.class.isInstance( getDialect() ) ) { + else if ( AbstractHANADialect.class.isInstance( dialect ) ) { assertSame( - ( (AbstractHANADialect) getDialect() ).getBlobTypeDescriptor(), + ( (AbstractHANADialect) dialect ).getBlobTypeDescriptor(), jdbcTypeRegistry.getDescriptor( Types.BLOB ) ); } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java index 8e90eec156..291c4f0ff7 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/junit/DialectFeatureChecks.java @@ -17,6 +17,7 @@ import org.hibernate.dialect.NationalizationSupport; import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.SQLServerDialect; +import org.hibernate.dialect.TimeZoneSupport; import org.hibernate.query.FetchClauseType; /** @@ -269,7 +270,7 @@ abstract public class DialectFeatureChecks { public static class SupportsTimezoneTypes implements DialectFeatureCheck { public boolean apply(Dialect dialect) { - return dialect.supportsTimezoneTypes(); + return dialect.getTimeZoneSupport() == TimeZoneSupport.NATIVE; } }