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
This commit is contained in:
Gavin 2022-12-05 20:17:55 +01:00 committed by Gavin King
parent a4f301814b
commit 863dda5cf5
4 changed files with 83 additions and 51 deletions

View File

@ -49,6 +49,15 @@ public enum TimeZoneStorageType {
* {@link org.hibernate.dialect.TimeZoneSupport#NATIVE}, * {@link org.hibernate.dialect.TimeZoneSupport#NATIVE},
* otherwise uses the {@link #COLUMN} strategy. * 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
} }

View File

@ -71,6 +71,7 @@ import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.CollectionClassification; import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.query.sqm.function.SqmFunctionDescriptor; import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.spi.ServiceException;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
import org.hibernate.usertype.UserType; import org.hibernate.usertype.UserType;
@ -538,7 +539,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
private final StandardServiceRegistry serviceRegistry; private final StandardServiceRegistry serviceRegistry;
private final MappingDefaultsImpl mappingDefaults; private final MappingDefaultsImpl mappingDefaults;
private final IdentifierGeneratorFactory identifierGeneratorFactory; private final IdentifierGeneratorFactory identifierGeneratorFactory;
private final TimeZoneStorageStrategy defaultTimezoneStorage; private final TimeZoneStorageType defaultTimezoneStorage;
// todo (6.0) : remove bootstrapContext property along with the deprecated methods // todo (6.0) : remove bootstrapContext property along with the deprecated methods
private BootstrapContext bootstrapContext; private BootstrapContext bootstrapContext;
@ -573,7 +574,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
this.mappingDefaults = new MappingDefaultsImpl( serviceRegistry ); this.mappingDefaults = new MappingDefaultsImpl( serviceRegistry );
this.defaultTimezoneStorage = resolveTimeZoneStorageStrategy( serviceRegistry, configService ); this.defaultTimezoneStorage = resolveTimeZoneStorageStrategy( configService );
this.multiTenancyEnabled = serviceRegistry.getService(MultiTenantConnectionProvider.class)!=null; this.multiTenancyEnabled = serviceRegistry.getService(MultiTenantConnectionProvider.class)!=null;
this.xmlMappingEnabled = configService.getSetting( this.xmlMappingEnabled = configService.getSetting(
@ -745,7 +746,55 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
@Override @Override
public TimeZoneStorageStrategy getDefaultTimeZoneStorage() { 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 @Override
@ -861,56 +910,12 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
} }
} }
private static TimeZoneStorageStrategy resolveTimeZoneStorageStrategy( private static TimeZoneStorageType resolveTimeZoneStorageStrategy(
StandardServiceRegistry serviceRegistry,
ConfigurationService configService) { ConfigurationService configService) {
final TimeZoneStorageType configuredTimeZoneStorageType = configService.getSetting( return configService.getSetting(
AvailableSettings.TIMEZONE_DEFAULT_STORAGE, AvailableSettings.TIMEZONE_DEFAULT_STORAGE,
value -> TimeZoneStorageType.valueOf( value.toString() ), 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;
} }
} }

View File

@ -22,6 +22,9 @@ import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.Id; 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.dialect.H2Dialect;
import org.hibernate.query.Query; import org.hibernate.query.Query;
import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.MariaDBDialect;
@ -42,6 +45,12 @@ import static org.junit.Assert.assertThat;
@TestForIssue(jiraKey = "HHH-10372") @TestForIssue(jiraKey = "HHH-10372")
public class OffsetDateTimeTest extends AbstractJavaTimeTypeTest<OffsetDateTime, OffsetDateTimeTest.EntityWithOffsetDateTime> { public class OffsetDateTimeTest extends AbstractJavaTimeTypeTest<OffsetDateTime, OffsetDateTimeTest.EntityWithOffsetDateTime> {
@Override
protected void configure(Configuration configuration) {
super.configure(configuration);
configuration.setProperty( AvailableSettings.TIMEZONE_DEFAULT_STORAGE, TimeZoneStorageType.NORMALIZE.toString() );
}
private static class ParametersBuilder extends AbstractParametersBuilder<ParametersBuilder> { private static class ParametersBuilder extends AbstractParametersBuilder<ParametersBuilder> {
public ParametersBuilder add(int year, int month, int day, public ParametersBuilder add(int year, int month, int day,
int hour, int minute, int second, int nanosecond, String offset, ZoneId defaultTimeZone) { int hour, int minute, int second, int nanosecond, String offset, ZoneId defaultTimeZone) {

View File

@ -22,6 +22,9 @@ import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.Id; 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.dialect.H2Dialect;
import org.hibernate.query.Query; import org.hibernate.query.Query;
import org.hibernate.dialect.MariaDBDialect; import org.hibernate.dialect.MariaDBDialect;
@ -42,6 +45,12 @@ import static org.junit.Assert.assertThat;
@TestForIssue(jiraKey = "HHH-10372") @TestForIssue(jiraKey = "HHH-10372")
public class ZonedDateTimeTest extends AbstractJavaTimeTypeTest<ZonedDateTime, ZonedDateTimeTest.EntityWithZonedDateTime> { public class ZonedDateTimeTest extends AbstractJavaTimeTypeTest<ZonedDateTime, ZonedDateTimeTest.EntityWithZonedDateTime> {
@Override
protected void configure(Configuration configuration) {
super.configure(configuration);
configuration.setProperty( AvailableSettings.TIMEZONE_DEFAULT_STORAGE, TimeZoneStorageType.NORMALIZE.toString() );
}
private static class ParametersBuilder extends AbstractParametersBuilder<ParametersBuilder> { private static class ParametersBuilder extends AbstractParametersBuilder<ParametersBuilder> {
public ParametersBuilder add(int year, int month, int day, public ParametersBuilder add(int year, int month, int day,
int hour, int minute, int second, int nanosecond, String zone, ZoneId defaultTimeZone) { int hour, int minute, int second, int nanosecond, String zone, ZoneId defaultTimeZone) {