From 863dda5cf54a26bf6043203030cc3664815232f8 Mon Sep 17 00:00:00 2001 From: Gavin Date: Mon, 5 Dec 2022 20:17:55 +0100 Subject: [PATCH] HHH-15820 make the default TimeZoneStorageStrategy for [Zoned|Offset]DateTime depend on the dialect Define TimeZoneStorageType.DEFAULT, uses NATIVE if possible, falls back to NORMALIZE_UTC so that for dialects with proper support for TIMESTAMP WITH TIME ZONE we use it this is consistent with what we already do for Instant --- .../annotations/TimeZoneStorageType.java | 11 +- .../boot/internal/MetadataBuilderImpl.java | 105 +++++++++--------- .../orm/test/type/OffsetDateTimeTest.java | 9 ++ .../orm/test/type/ZonedDateTimeTest.java | 9 ++ 4 files changed, 83 insertions(+), 51 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/annotations/TimeZoneStorageType.java b/hibernate-core/src/main/java/org/hibernate/annotations/TimeZoneStorageType.java index 1b7fd335fe..71a2901e80 100644 --- a/hibernate-core/src/main/java/org/hibernate/annotations/TimeZoneStorageType.java +++ b/hibernate-core/src/main/java/org/hibernate/annotations/TimeZoneStorageType.java @@ -49,6 +49,15 @@ public enum TimeZoneStorageType { * {@link org.hibernate.dialect.TimeZoneSupport#NATIVE}, * otherwise uses the {@link #COLUMN} strategy. */ - AUTO + AUTO, + /** + * Stores the time zone either with {@link #NATIVE} if + * {@link Dialect#getTimeZoneSupport()} is + * {@link org.hibernate.dialect.TimeZoneSupport#NATIVE}, + * otherwise uses the {@link #NORMALIZE_UTC} strategy. + * + * @since 6.2 + */ + DEFAULT } 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 7b0864adaf..9743ec5935 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 @@ -71,6 +71,7 @@ import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.metamodel.CollectionClassification; import org.hibernate.query.sqm.function.SqmFunctionDescriptor; import org.hibernate.service.ServiceRegistry; +import org.hibernate.service.spi.ServiceException; import org.hibernate.type.BasicType; import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.usertype.UserType; @@ -538,7 +539,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont private final StandardServiceRegistry serviceRegistry; private final MappingDefaultsImpl mappingDefaults; private final IdentifierGeneratorFactory identifierGeneratorFactory; - private final TimeZoneStorageStrategy defaultTimezoneStorage; + private final TimeZoneStorageType defaultTimezoneStorage; // todo (6.0) : remove bootstrapContext property along with the deprecated methods private BootstrapContext bootstrapContext; @@ -573,7 +574,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont this.mappingDefaults = new MappingDefaultsImpl( serviceRegistry ); - this.defaultTimezoneStorage = resolveTimeZoneStorageStrategy( serviceRegistry, configService ); + this.defaultTimezoneStorage = resolveTimeZoneStorageStrategy( configService ); this.multiTenancyEnabled = serviceRegistry.getService(MultiTenantConnectionProvider.class)!=null; this.xmlMappingEnabled = configService.getSetting( @@ -745,7 +746,55 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont @Override public TimeZoneStorageStrategy getDefaultTimeZoneStorage() { - return defaultTimezoneStorage; + return toTimeZoneStorageStrategy( getTimeZoneSupport( serviceRegistry ) ); + } + + private static TimeZoneSupport getTimeZoneSupport(StandardServiceRegistry serviceRegistry) { + try { + return serviceRegistry.getService( JdbcServices.class ) + .getDialect() + .getTimeZoneSupport(); + } + catch (ServiceException se) { + return TimeZoneSupport.NONE; + } + } + private TimeZoneStorageStrategy toTimeZoneStorageStrategy(TimeZoneSupport timeZoneSupport) { + switch ( defaultTimezoneStorage ) { + case NATIVE: + if ( timeZoneSupport != TimeZoneSupport.NATIVE ) { + throw new HibernateException( "The configured time zone storage type NATIVE is not supported with the configured dialect" ); + } + return TimeZoneStorageStrategy.NATIVE; + case COLUMN: + return TimeZoneStorageStrategy.COLUMN; + case NORMALIZE: + return TimeZoneStorageStrategy.NORMALIZE; + case NORMALIZE_UTC: + return TimeZoneStorageStrategy.NORMALIZE_UTC; + case AUTO: + switch (timeZoneSupport) { + case NATIVE: + return TimeZoneStorageStrategy.NATIVE; + case NORMALIZE: + case NONE: + return TimeZoneStorageStrategy.COLUMN; + default: + throw new HibernateException( "Unsupported time zone support: " + timeZoneSupport); + } + case DEFAULT: + switch (timeZoneSupport) { + case NATIVE: + return TimeZoneStorageStrategy.NATIVE; + case NORMALIZE: + case NONE: + return TimeZoneStorageStrategy.NORMALIZE_UTC; + default: + throw new HibernateException( "Unsupported time zone support: " + timeZoneSupport); + } + default: + throw new HibernateException( "Unsupported time zone storage type: " + defaultTimezoneStorage ); + } } @Override @@ -861,56 +910,12 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont } } - private static TimeZoneStorageStrategy resolveTimeZoneStorageStrategy( - StandardServiceRegistry serviceRegistry, + private static TimeZoneStorageType resolveTimeZoneStorageStrategy( ConfigurationService configService) { - final TimeZoneStorageType configuredTimeZoneStorageType = configService.getSetting( + return configService.getSetting( AvailableSettings.TIMEZONE_DEFAULT_STORAGE, value -> TimeZoneStorageType.valueOf( value.toString() ), - null + TimeZoneStorageType.DEFAULT ); - 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 NORMALIZE_UTC: - resolvedTimezoneStorage = TimeZoneStorageStrategy.NORMALIZE_UTC; - 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/test/java/org/hibernate/orm/test/type/OffsetDateTimeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/OffsetDateTimeTest.java index 0eec2eea53..5172a7538d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/type/OffsetDateTimeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/OffsetDateTimeTest.java @@ -22,6 +22,9 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; +import org.hibernate.annotations.TimeZoneStorageType; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; import org.hibernate.dialect.H2Dialect; import org.hibernate.query.Query; import org.hibernate.dialect.MariaDBDialect; @@ -42,6 +45,12 @@ import static org.junit.Assert.assertThat; @TestForIssue(jiraKey = "HHH-10372") public class OffsetDateTimeTest extends AbstractJavaTimeTypeTest { + @Override + protected void configure(Configuration configuration) { + super.configure(configuration); + configuration.setProperty( AvailableSettings.TIMEZONE_DEFAULT_STORAGE, TimeZoneStorageType.NORMALIZE.toString() ); + } + private static class ParametersBuilder extends AbstractParametersBuilder { public ParametersBuilder add(int year, int month, int day, int hour, int minute, int second, int nanosecond, String offset, ZoneId defaultTimeZone) { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/type/ZonedDateTimeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/type/ZonedDateTimeTest.java index da649141d7..2cf5ea53ff 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/type/ZonedDateTimeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/type/ZonedDateTimeTest.java @@ -22,6 +22,9 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id; +import org.hibernate.annotations.TimeZoneStorageType; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; import org.hibernate.dialect.H2Dialect; import org.hibernate.query.Query; import org.hibernate.dialect.MariaDBDialect; @@ -42,6 +45,12 @@ import static org.junit.Assert.assertThat; @TestForIssue(jiraKey = "HHH-10372") public class ZonedDateTimeTest extends AbstractJavaTimeTypeTest { + @Override + protected void configure(Configuration configuration) { + super.configure(configuration); + configuration.setProperty( AvailableSettings.TIMEZONE_DEFAULT_STORAGE, TimeZoneStorageType.NORMALIZE.toString() ); + } + private static class ParametersBuilder extends AbstractParametersBuilder { public ParametersBuilder add(int year, int month, int day, int hour, int minute, int second, int nanosecond, String zone, ZoneId defaultTimeZone) {