misc javadoc and refactorings around TimeZoneStorage stuff

This commit is contained in:
Gavin 2022-12-05 20:18:23 +01:00 committed by Gavin King
parent 863dda5cf5
commit e53b55583b
16 changed files with 258 additions and 215 deletions

View File

@ -7,16 +7,20 @@
package org.hibernate;
/**
* Describes the storage strategies understood by Hibernate.
* Enumerates the possible storage strategies for offset or zoned datetimes.
*
* @author Christian Beikov
* @author Steve Ebersole
* @author Andrea Boriero
*
* @see org.hibernate.annotations.TimeZoneStorageType
* @see org.hibernate.dialect.TimeZoneSupport
*/
@Incubating
public enum TimeZoneStorageStrategy {
/**
* Stores the time zone through the "with time zone" types which retain the information.
* Stores the time zone via the {@code with time zone} SQL types which retain
* the information.
*/
NATIVE,
/**

View File

@ -16,10 +16,11 @@ import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
/**
* Specifies the mapped column for storing the time zone information.
* The annotation can be used in conjunction with the <code>TimeZoneStorageType.AUTO</code> and
* <code>TimeZoneStorageType.COLUMN</code>. The column is simply ignored if <code>TimeZoneStorageType.AUTO</code>
* is used and the database supports native time zone storage.
* Specifies the mapped column for storing the time zone information,
* for use in conjunction with {@link TimeZoneStorageType#COLUMN} or
* {@link TimeZoneStorageType#AUTO}. The column is simply ignored if
* {@code AUTO} is used and the database supports native time zone
* storage.
*
* @author Christian Beikov
* @author Steve Ebersole

View File

@ -17,22 +17,27 @@ import static java.lang.annotation.ElementType.METHOD;
/**
* Specifies how the time zone information of a persistent property or field should be persisted.
* The <code>TimeZoneStorage</code> annotation may be used in conjunction with the <code>Basic</code> annotation, or in
* conjunction with the <code>ElementCollection</code> annotation when the
* element collection value is of basic type. If the <code>TimeZoneStorage</code> annotation is not
* used, the <code>TimeZoneStorageType</code> value is assumed to be <code>NORMALIZED</code>.
* This annotation may be used in conjunction with the {@link jakarta.persistence.Basic} annotation,
* or in conjunction with the {@link jakarta.persistence.ElementCollection} annotation when the
* element collection value is of basic type. If the {@code TimeZoneStorage} annotation is not used,
* the {@link TimeZoneStorageType} has a default value determined by the dialect and by the
* configuration property {@value org.hibernate.cfg.AvailableSettings#TIMEZONE_DEFAULT_STORAGE}.
*
* For example:
* <pre>
* Example:
* {@code
* @Entity public class Person {
*
* &#064;Entity public class Person {
* public OffsetDateTime getBirthDateTimeNormalized() {...}
* @TimeZoneStorage(COLUMN)
* @TimeZoneColumn(column = @Column(...))
* public OffsetDateTime birthDate;
*
* @TimeZoneStorage(NATIVE)
* public OffsetDateTime registrationDate;
*
* &#064;TimeZoneStorage
* &#064;TimeZoneColumn(column = &#064;Column(...))
* public OffsetDateTime getBirthDateTimeNativeOrColumn() {...}
* ...
* }
*}
* </pre>
*
* @author Christian Beikov

View File

@ -11,19 +11,27 @@ import org.hibernate.dialect.Dialect;
/**
* Describes the storage of timezone information for zoned datetime types.
* <p>
* A default {@code TimeZoneStorageType} may be configured explicitly using
* {@value org.hibernate.cfg.AvailableSettings#TIMEZONE_DEFAULT_STORAGE}.
* Otherwise, the storage type may be overridden for a given field or
* property of an entity using the {@link TimeZoneStorage} annotation.
*
* @author Christian Beikov
* @author Steve Ebersole
* @author Andrea Boriero
*
* @since 6.0
*
* @see TimeZoneStorage
* @see org.hibernate.TimeZoneStorageStrategy
*/
@Incubating
public enum TimeZoneStorageType {
/**
* Stores the timezone by using the {@code with time zone}
* SQL column type.
*
* <p>
* Error if {@link Dialect#getTimeZoneSupport()} is not
* {@link org.hibernate.dialect.TimeZoneSupport#NATIVE}.
*/
@ -34,8 +42,11 @@ public enum TimeZoneStorageType {
*/
NORMALIZE,
/**
* Does not store the time zone, and instead normalizes
* Does not preserve the time zone, and instead normalizes
* timestamps to UTC.
* <p>
* The DDL column type depends on the setting
* {@value org.hibernate.cfg.AvailableSettings#PREFERRED_INSTANT_JDBC_TYPE}.
*/
NORMALIZE_UTC,
/**
@ -59,5 +70,4 @@ public enum TimeZoneStorageType {
* @since 6.2
*/
DEFAULT
}

View File

@ -63,19 +63,7 @@ public class VersionResolution<E> implements BasicValue.Resolution<E> {
@Override
public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() {
if ( timeZoneStorageType != null ) {
switch ( timeZoneStorageType ) {
case COLUMN:
return TimeZoneStorageStrategy.COLUMN;
case NATIVE:
return TimeZoneStorageStrategy.NATIVE;
case NORMALIZE:
return TimeZoneStorageStrategy.NORMALIZE;
case NORMALIZE_UTC:
return TimeZoneStorageStrategy.NORMALIZE_UTC;
}
}
return context.getBuildingOptions().getDefaultTimeZoneStorage();
return BasicValue.timeZoneStorageStrategy( timeZoneStorageType, context );
}
@Override

View File

@ -30,6 +30,7 @@ import org.hibernate.boot.model.source.internal.hbm.HbmMetadataSourceProcessorIm
import org.hibernate.boot.model.source.internal.hbm.MappingDocument;
import org.hibernate.boot.model.source.internal.hbm.ModelBinder;
import org.hibernate.boot.model.source.spi.MetadataSourceProcessor;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.spi.AdditionalJaxbMappingProducer;
import org.hibernate.boot.spi.BootstrapContext;
@ -42,7 +43,6 @@ 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.internal.util.config.ConfigurationHelper;
import org.hibernate.type.BasicType;
import org.hibernate.type.BasicTypeRegistry;
import org.hibernate.type.SqlTypes;
@ -60,6 +60,11 @@ import org.hibernate.type.spi.TypeConfiguration;
import org.jboss.jandex.IndexView;
import org.jboss.logging.Logger;
import static org.hibernate.internal.util.config.ConfigurationHelper.getPreferredSqlTypeCodeForArray;
import static org.hibernate.internal.util.config.ConfigurationHelper.getPreferredSqlTypeCodeForDuration;
import static org.hibernate.internal.util.config.ConfigurationHelper.getPreferredSqlTypeCodeForInstant;
import static org.hibernate.internal.util.config.ConfigurationHelper.getPreferredSqlTypeCodeForUuid;
/**
* Represents the process of of transforming a {@link MetadataSources}
* reference into a {@link org.hibernate.boot.Metadata} reference. Allows for 2 different process paradigms:<ul>
@ -379,15 +384,13 @@ public class MetadataBuildingProcess {
final ClassLoaderService classLoaderService = options.getServiceRegistry().getService( ClassLoaderService.class );
final TypeConfiguration typeConfiguration = bootstrapContext.getTypeConfiguration();
final StandardServiceRegistry serviceRegistry = bootstrapContext.getServiceRegistry();
final JdbcTypeRegistry jdbcTypeRegistry = typeConfiguration.getJdbcTypeRegistry();
final TypeContributions typeContributions = () -> typeConfiguration;
// add Dialect contributed types
final Dialect dialect = options.getServiceRegistry().getService( JdbcServices.class ).getDialect();
dialect.contributeTypes( typeContributions, options.getServiceRegistry() );
final JdbcType dialectUuidDescriptor = jdbcTypeRegistry.findDescriptor( SqlTypes.UUID );
final JdbcType dialectArrayDescriptor = jdbcTypeRegistry.findDescriptor( SqlTypes.ARRAY );
final JdbcType dialectIntervalDescriptor = jdbcTypeRegistry.findDescriptor( SqlTypes.INTERVAL_SECOND );
// add TypeContributor contributed types.
for ( TypeContributor contributor : classLoaderService.loadJavaServices( TypeContributor.class ) ) {
@ -395,51 +398,38 @@ public class MetadataBuildingProcess {
}
// add fallback type descriptors
final int preferredSqlTypeCodeForUuid = ConfigurationHelper.getPreferredSqlTypeCodeForUuid( bootstrapContext.getServiceRegistry() );
final int preferredSqlTypeCodeForUuid = getPreferredSqlTypeCodeForUuid( serviceRegistry );
if ( preferredSqlTypeCodeForUuid != SqlTypes.UUID ) {
if ( jdbcTypeRegistry.findDescriptor( SqlTypes.UUID ) == dialectUuidDescriptor ) {
jdbcTypeRegistry.addDescriptor(
SqlTypes.UUID,
jdbcTypeRegistry.getDescriptor( preferredSqlTypeCodeForUuid )
);
}
adaptToPreferredSqlTypeCode( jdbcTypeRegistry, SqlTypes.UUID, preferredSqlTypeCodeForUuid );
}
else {
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.UUID, SqlTypes.BINARY );
}
jdbcTypeRegistry.addDescriptorIfAbsent( JsonJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptorIfAbsent( XmlAsStringJdbcType.INSTANCE );
final int preferredSqlTypeCodeForArray = ConfigurationHelper.getPreferredSqlTypeCodeForArray( bootstrapContext.getServiceRegistry() );
if ( preferredSqlTypeCodeForArray != SqlTypes.ARRAY ) {
if ( jdbcTypeRegistry.findDescriptor( SqlTypes.ARRAY ) == dialectArrayDescriptor ) {
jdbcTypeRegistry.addDescriptor(
SqlTypes.ARRAY,
jdbcTypeRegistry.getDescriptor( preferredSqlTypeCodeForArray )
);
final int preferredSqlTypeCodeForArray = getPreferredSqlTypeCodeForArray( serviceRegistry );
if ( preferredSqlTypeCodeForArray == SqlTypes.ARRAY ) {
adaptToPreferredSqlTypeCode( jdbcTypeRegistry, null, SqlTypes.ARRAY, SqlTypes.VARBINARY );
}
else {
adaptToPreferredSqlTypeCode( jdbcTypeRegistry, SqlTypes.ARRAY, preferredSqlTypeCodeForArray );
}
else if ( jdbcTypeRegistry.findDescriptor( SqlTypes.ARRAY ) == null ) {
// Fallback to VARBINARY
jdbcTypeRegistry.addDescriptor( SqlTypes.ARRAY, jdbcTypeRegistry.getDescriptor( SqlTypes.VARBINARY ) );
}
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INET, SqlTypes.VARBINARY );
final int preferredSqlTypeCodeForDuration = ConfigurationHelper.getPreferredSqlTypeCodeForDuration( bootstrapContext.getServiceRegistry() );
final int preferredSqlTypeCodeForDuration = getPreferredSqlTypeCodeForDuration( serviceRegistry );
if ( preferredSqlTypeCodeForDuration != SqlTypes.INTERVAL_SECOND ) {
if ( jdbcTypeRegistry.findDescriptor( SqlTypes.INTERVAL_SECOND ) == dialectIntervalDescriptor ) {
jdbcTypeRegistry.addDescriptor(
SqlTypes.INTERVAL_SECOND,
jdbcTypeRegistry.getDescriptor( preferredSqlTypeCodeForDuration )
);
}
adaptToPreferredSqlTypeCode( jdbcTypeRegistry, SqlTypes.INTERVAL_SECOND, preferredSqlTypeCodeForDuration );
}
else {
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INTERVAL_SECOND, SqlTypes.NUMERIC );
}
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.INET, SqlTypes.VARBINARY );
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.GEOMETRY, SqlTypes.VARBINARY );
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.POINT, SqlTypes.VARBINARY );
addFallbackIfNecessary( jdbcTypeRegistry, SqlTypes.GEOGRAPHY, SqlTypes.GEOMETRY );
jdbcTypeRegistry.addDescriptorIfAbsent( JsonJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptorIfAbsent( XmlAsStringJdbcType.INSTANCE );
final DdlTypeRegistry ddlTypeRegistry = typeConfiguration.getDdlTypeRegistry();
// Fallback to the biggest varchar DdlType when json is requested
ddlTypeRegistry.addDescriptorIfAbsent(
@ -471,21 +461,63 @@ public class MetadataBuildingProcess {
// add explicit application registered types
typeConfiguration.addBasicTypeRegistrationContributions( options.getBasicTypeRegistrations() );
final JdbcType timestampWithTimeZoneOverride;
switch ( options.getDefaultTimeZoneStorage() ) {
case NORMALIZE:
// For NORMALIZE, we replace the standard types that use TIMESTAMP_WITH_TIMEZONE to use TIMESTAMP
timestampWithTimeZoneOverride = jdbcTypeRegistry.getDescriptor( Types.TIMESTAMP );
break;
case NORMALIZE_UTC:
// For NORMALIZE_UTC, we replace the standard types that use TIMESTAMP_WITH_TIMEZONE to use TIMESTAMP_UTC
timestampWithTimeZoneOverride = jdbcTypeRegistry.getDescriptor( SqlTypes.TIMESTAMP_UTC );
break;
default:
timestampWithTimeZoneOverride = null;
break;
}
final JdbcType timestampWithTimeZoneOverride = getTimestampWithTimeZoneOverride( options, jdbcTypeRegistry );
if ( timestampWithTimeZoneOverride != null ) {
adaptToDefaultTimeZoneStorage( typeConfiguration, timestampWithTimeZoneOverride );
}
final int preferredSqlTypeCodeForInstant = getPreferredSqlTypeCodeForInstant(serviceRegistry);
if ( preferredSqlTypeCodeForInstant != SqlTypes.TIMESTAMP_UTC ) {
adaptToPreferredSqlTypeCodeForInstant( typeConfiguration, jdbcTypeRegistry, preferredSqlTypeCodeForInstant );
}
}
private static void adaptToPreferredSqlTypeCode(
JdbcTypeRegistry jdbcTypeRegistry,
int defaultSqlTypeCode,
int preferredSqlTypeCode) {
adaptToPreferredSqlTypeCode(
jdbcTypeRegistry,
jdbcTypeRegistry.findDescriptor( defaultSqlTypeCode ),
defaultSqlTypeCode,
preferredSqlTypeCode
);
}
private static void adaptToPreferredSqlTypeCode(
JdbcTypeRegistry jdbcTypeRegistry,
JdbcType dialectUuidDescriptor,
int defaultSqlTypeCode,
int preferredSqlTypeCode) {
if ( jdbcTypeRegistry.findDescriptor( defaultSqlTypeCode ) == dialectUuidDescriptor ) {
jdbcTypeRegistry.addDescriptor(
defaultSqlTypeCode,
jdbcTypeRegistry.getDescriptor( preferredSqlTypeCode )
);
}
}
private static void adaptToPreferredSqlTypeCodeForInstant(
TypeConfiguration typeConfiguration,
JdbcTypeRegistry jdbcTypeRegistry,
int preferredSqlTypeCodeForInstant) {
final JavaTypeRegistry javaTypeRegistry = typeConfiguration.getJavaTypeRegistry();
final BasicTypeRegistry basicTypeRegistry = typeConfiguration.getBasicTypeRegistry();
final BasicType<?> instantType = new NamedBasicTypeImpl<>(
javaTypeRegistry.getDescriptor( Instant.class ),
jdbcTypeRegistry.getDescriptor( preferredSqlTypeCodeForInstant ),
"instant"
);
basicTypeRegistry.register(
instantType,
"org.hibernate.type.InstantType",
Instant.class.getSimpleName(),
Instant.class.getName()
);
}
private static void adaptToDefaultTimeZoneStorage(
TypeConfiguration typeConfiguration,
JdbcType timestampWithTimeZoneOverride) {
final JavaTypeRegistry javaTypeRegistry = typeConfiguration.getJavaTypeRegistry();
final BasicTypeRegistry basicTypeRegistry = typeConfiguration.getBasicTypeRegistry();
final BasicType<?> offsetDateTimeType = new NamedBasicTypeImpl<>(
@ -511,21 +543,17 @@ public class MetadataBuildingProcess {
ZonedDateTime.class.getName()
);
}
final int preferredSqlTypeCodeForInstant = ConfigurationHelper.getPreferredSqlTypeCodeForInstant( bootstrapContext.getServiceRegistry() );
if ( preferredSqlTypeCodeForInstant != SqlTypes.TIMESTAMP_UTC ) {
final JavaTypeRegistry javaTypeRegistry = typeConfiguration.getJavaTypeRegistry();
final BasicTypeRegistry basicTypeRegistry = typeConfiguration.getBasicTypeRegistry();
final BasicType<?> instantType = new NamedBasicTypeImpl<>(
javaTypeRegistry.getDescriptor( Instant.class ),
jdbcTypeRegistry.getDescriptor( preferredSqlTypeCodeForInstant ),
"instant"
);
basicTypeRegistry.register(
instantType,
"org.hibernate.type.InstantType",
Instant.class.getSimpleName(),
Instant.class.getName()
);
private static JdbcType getTimestampWithTimeZoneOverride(MetadataBuildingOptions options, JdbcTypeRegistry jdbcTypeRegistry) {
switch ( options.getDefaultTimeZoneStorage() ) {
case NORMALIZE:
// For NORMALIZE, we replace the standard types that use TIMESTAMP_WITH_TIMEZONE to use TIMESTAMP
return jdbcTypeRegistry.getDescriptor( Types.TIMESTAMP );
case NORMALIZE_UTC:
// For NORMALIZE_UTC, we replace the standard types that use TIMESTAMP_WITH_TIMEZONE to use TIMESTAMP_UTC
return jdbcTypeRegistry.getDescriptor( SqlTypes.TIMESTAMP_UTC );
default:
return null;
}
}

View File

@ -179,19 +179,7 @@ public class BasicValueBinder implements JdbcTypeIndicators {
}
@Override
public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() {
if ( timeZoneStorageType != null ) {
switch ( timeZoneStorageType ) {
case COLUMN:
return TimeZoneStorageStrategy.COLUMN;
case NATIVE:
return TimeZoneStorageStrategy.NATIVE;
case NORMALIZE:
return TimeZoneStorageStrategy.NORMALIZE;
case NORMALIZE_UTC:
return TimeZoneStorageStrategy.NORMALIZE_UTC;
}
}
return buildingContext.getBuildingOptions().getDefaultTimeZoneStorage();
return BasicValue.timeZoneStorageStrategy( timeZoneStorageType, buildingContext );
}

View File

@ -29,7 +29,7 @@ import org.hibernate.sql.exec.spi.JdbcOperation;
import java.util.List;
/**
* An SQL dialect for DB2 for iSeries previously known as DB2/400.
* An SQL dialect for DB2 for iSeries, previously known as "DB2/400".
*
* @author Peter DeGregorio (pdegregorio)
* @author Christian Beikov

View File

@ -33,7 +33,7 @@ import java.util.List;
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
/**
* An SQL dialect for DB2 for z/OS, previously known as known as Db2 UDB for z/OS and Db2 UDB for z/OS and OS/390.
* A SQL dialect for DB2 for z/OS, previously known as "Db2 UDB for z/OS" and as "Db2 UDB for z/OS and OS/390".
*
* @author Christian Beikov
*/

View File

@ -4197,8 +4197,8 @@ public abstract class Dialect implements ConversionContext {
* native datetime formatting function for this
* database (often the {@code to_char()} function).
* <p>
* Since it's never possible to translate all of
* the pattern letter sequences understood by
* Since it's never possible to translate every
* pattern letter sequences understood by
* {@code DateTimeFormatter}, only the following
* subset of pattern letters is accepted by
* Hibernate:

View File

@ -9,22 +9,31 @@ package org.hibernate.dialect;
import org.hibernate.Incubating;
/**
* Describes the support for "with time zone" types.
* Describes the extent to which a given database supports the SQL
* {@code with time zone} types.
* <p>
* Really we only care about {@code timestamp with time zone} here,
* since the type {@code time with time zone} is deeply conceptually
* questionable, and so Hibernate eschews its use.
*
* @author Christian Beikov
*/
@Incubating
public enum TimeZoneSupport {
/**
* The "with time zone" types retain the time zone information.
* The {@code with time zone} types retain the time zone information.
* That is, a round trip writing and reading a zoned datetime results
* in the exact same zoned datetime with the same timezone.
*/
NATIVE,
/**
* The "with time zone" types normalize to UTC.
* The {@code with time zone} types normalize to UTC. That is, a round
* trip writing and reading a zoned datetime results in a datetime
* representing the same instant, but in the timezone UTC.
*/
NORMALIZE,
/**
* No support for "with time zone" types.
* No support for {@code with time zone} types.
*/
NONE;
NONE
}

View File

@ -12,11 +12,11 @@ import java.util.Properties;
import java.util.function.Consumer;
import java.util.function.Function;
import org.hibernate.Internal;
import org.hibernate.MappingException;
import org.hibernate.TimeZoneStorageStrategy;
import org.hibernate.annotations.TimeZoneStorageType;
import org.hibernate.boot.model.TypeDefinition;
import org.hibernate.boot.model.TypeDefinitionRegistry;
import org.hibernate.boot.model.convert.internal.ClassBasedConverterDescriptor;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.model.convert.spi.JpaAttributeConverterCreationContext;
@ -700,6 +700,13 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
@Override
public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() {
return timeZoneStorageStrategy( timeZoneStorageType, getBuildingContext() );
}
@Internal
public static TimeZoneStorageStrategy timeZoneStorageStrategy(
TimeZoneStorageType timeZoneStorageType,
MetadataBuildingContext buildingContext) {
if ( timeZoneStorageType != null ) {
switch ( timeZoneStorageType ) {
case COLUMN:
@ -712,7 +719,7 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
return TimeZoneStorageStrategy.NORMALIZE_UTC;
}
}
return getBuildingContext().getBuildingOptions().getDefaultTimeZoneStorage();
return buildingContext.getBuildingOptions().getDefaultTimeZoneStorage();
}
@Override

View File

@ -7,13 +7,11 @@
package org.hibernate.type.descriptor.java;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
@ -22,13 +20,13 @@ import jakarta.persistence.TemporalType;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.spi.TypeConfiguration;
import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME;
/**
* Java type descriptor for the {@link OffsetDateTime} type.
*
@ -52,44 +50,24 @@ public class OffsetDateTimeJavaType extends AbstractTemporalJavaType<OffsetDateT
@Override
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators stdIndicators) {
final TemporalType temporalPrecision = stdIndicators.getTemporalPrecision();
final JdbcTypeRegistry jdbcTypeRegistry = stdIndicators.getTypeConfiguration()
.getJdbcTypeRegistry();
if ( temporalPrecision == null || temporalPrecision == TemporalType.TIMESTAMP ) {
switch ( stdIndicators.getDefaultTimeZoneStorageStrategy() ) {
case NORMALIZE:
return jdbcTypeRegistry.getDescriptor( Types.TIMESTAMP );
case NORMALIZE_UTC:
return jdbcTypeRegistry.getDescriptor( SqlTypes.TIMESTAMP_UTC );
default:
return jdbcTypeRegistry.getDescriptor( Types.TIMESTAMP_WITH_TIMEZONE );
}
}
switch ( temporalPrecision ) {
case TIME:
return jdbcTypeRegistry.getDescriptor( Types.TIME );
case DATE:
return jdbcTypeRegistry.getDescriptor( Types.DATE );
default:
throw new IllegalArgumentException( "Unexpected jakarta.persistence.TemporalType : " + temporalPrecision );
}
return stdIndicators.getTypeConfiguration().getJdbcTypeRegistry()
.getDescriptor( stdIndicators.getDefaultZonedTimestampSqlType() );
}
@Override
@SuppressWarnings("unchecked")
protected <X> TemporalJavaType<X> forTimestampPrecision(TypeConfiguration typeConfiguration) {
//noinspection unchecked
return (TemporalJavaType<X>) this;
}
@Override
public String toString(OffsetDateTime value) {
return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format( value );
return ISO_OFFSET_DATE_TIME.format( value );
}
@Override
public OffsetDateTime fromString(CharSequence string) {
return OffsetDateTime.from( DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse( string ) );
return OffsetDateTime.from( ISO_OFFSET_DATE_TIME.parse( string ) );
}
@Override

View File

@ -7,13 +7,11 @@
package org.hibernate.type.descriptor.java;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
@ -23,13 +21,13 @@ import jakarta.persistence.TemporalType;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.ZonedDateTimeComparator;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.spi.TypeConfiguration;
import static java.time.format.DateTimeFormatter.ISO_ZONED_DATE_TIME;
/**
* Java type descriptor for the {@link ZonedDateTime} type.
*
@ -52,47 +50,24 @@ public class ZonedDateTimeJavaType extends AbstractTemporalJavaType<ZonedDateTim
@Override
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators stdIndicators) {
final TemporalType temporalPrecision = stdIndicators.getTemporalPrecision();
final JdbcTypeRegistry jdbcTypeRegistry = stdIndicators.getTypeConfiguration()
.getJdbcTypeRegistry();
if ( temporalPrecision == null || temporalPrecision == TemporalType.TIMESTAMP ) {
switch ( stdIndicators.getDefaultTimeZoneStorageStrategy() ) {
case NORMALIZE:
return jdbcTypeRegistry.getDescriptor( Types.TIMESTAMP );
case NORMALIZE_UTC:
return jdbcTypeRegistry.getDescriptor( SqlTypes.TIMESTAMP_UTC );
default:
return jdbcTypeRegistry.getDescriptor( Types.TIMESTAMP_WITH_TIMEZONE );
}
}
switch ( temporalPrecision ) {
case TIME: {
return jdbcTypeRegistry.getDescriptor( Types.TIME );
}
case DATE: {
return jdbcTypeRegistry.getDescriptor( Types.DATE );
}
default: {
throw new IllegalArgumentException( "Unexpected jakarta.persistence.TemporalType : " + temporalPrecision );
}
}
return stdIndicators.getTypeConfiguration().getJdbcTypeRegistry()
.getDescriptor( stdIndicators.getDefaultZonedTimestampSqlType() );
}
@Override
@SuppressWarnings("unchecked")
protected <X> TemporalJavaType<X> forTimestampPrecision(TypeConfiguration typeConfiguration) {
//noinspection unchecked
return (TemporalJavaType<X>) this;
}
@Override
public String toString(ZonedDateTime value) {
return DateTimeFormatter.ISO_ZONED_DATE_TIME.format( value );
return ISO_ZONED_DATE_TIME.format( value );
}
@Override
public ZonedDateTime fromString(CharSequence string) {
return ZonedDateTime.from( DateTimeFormatter.ISO_ZONED_DATE_TIME.parse( string ) );
return ZonedDateTime.from( ISO_ZONED_DATE_TIME.parse( string ) );
}
@Override

View File

@ -9,11 +9,15 @@ package org.hibernate.type.descriptor.jdbc;
import jakarta.persistence.EnumType;
import jakarta.persistence.TemporalType;
import org.hibernate.AssertionFailure;
import org.hibernate.TimeZoneStorageStrategy;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.java.BasicJavaType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.spi.TypeConfiguration;
import java.sql.Types;
/**
* A parameter object that helps determine the {@link java.sql.Types SQL/JDBC type}
* recommended by the JDBC spec (explicitly or implicitly) for a given Java type.
@ -123,7 +127,7 @@ public interface JdbcTypeIndicators {
/**
* Useful for resolutions based on column length.
*
* <p>
* E.g. for choosing between a {@code VARCHAR} ({@code String}) and {@code CHAR(1)} ({@code Character}/{@code char}).
*/
default long getColumnLength() {
@ -139,7 +143,7 @@ public interface JdbcTypeIndicators {
/**
* Useful for resolutions based on column scale.
*
* <p>
* E.g. for choosing between a {@code NUMERIC} and {@code INTERVAL SECOND}.
*/
default int getColumnScale() {
@ -174,4 +178,50 @@ public interface JdbcTypeIndicators {
private JdbcTypeIndicators getCurrentBaseSqlTypeIndicators() {
return getTypeConfiguration().getCurrentBaseSqlTypeIndicators();
}
/**
* @return the SQL column type used for storing datetimes under the
* given {@linkplain TimeZoneStorageStrategy storage strategy}
*
* @see SqlTypes#TIME_WITH_TIMEZONE
* @see SqlTypes#TIMESTAMP
* @see SqlTypes#TIMESTAMP_UTC
*/
static int getZonedTimestampSqlType(TimeZoneStorageStrategy storageStrategy) {
switch ( storageStrategy ) {
case NATIVE:
return SqlTypes.TIMESTAMP_WITH_TIMEZONE;
case COLUMN:
case NORMALIZE:
return SqlTypes.TIMESTAMP;
case NORMALIZE_UTC:
// sensitive to hibernate.type.preferred_instant_jdbc_type
return SqlTypes.TIMESTAMP_UTC;
default:
throw new AssertionFailure( "unknown time zone storage strategy" );
}
}
/**
* @return the SQL column type used for storing datetimes under the
* default {@linkplain TimeZoneStorageStrategy storage strategy}
*
* @see SqlTypes#TIME_WITH_TIMEZONE
* @see SqlTypes#TIMESTAMP
* @see SqlTypes#TIMESTAMP_UTC
*/
default int getDefaultZonedTimestampSqlType() {
final TemporalType temporalPrecision = getTemporalPrecision();
switch ( temporalPrecision == null ? TemporalType.TIMESTAMP : temporalPrecision ) {
case TIME:
return Types.TIME;
case DATE:
return Types.DATE;
case TIMESTAMP:
// sensitive to hibernate.timezone.default_storage
return getZonedTimestampSqlType( getDefaultTimeZoneStorageStrategy() );
default:
throw new IllegalArgumentException( "Unexpected jakarta.persistence.TemporalType : " + temporalPrecision);
}
}
}

View File

@ -250,19 +250,19 @@ public class TypeConfiguration implements SessionFactoryObserver, Serializable {
}
/**
* Understands the following target type names for the cast() function:
*
* - String
* - Character
* - Byte, Integer, Long
* - Float, Double
* - Time, Date, Timestamp
* - LocalDate, LocalTime, LocalDateTime
* - BigInteger
* - BigDecimal
* - Binary
* - Boolean (fragile)
*
* Understands the following target type names for the {@code cast()} function:
* <ul>
* <li>String
* <li>Character
* <li>Byte, Integer, Long
* <li>Float, Double
* <li>Time, Date, Timestamp
* <li>LocalDate, LocalTime, LocalDateTime
* <li>BigInteger
* <li>BigDecimal
* <li>Binary
* <li>Boolean (fragile)
* </ul>
* (The type names are not case-sensitive.)
*/
public BasicValuedMapping resolveCastTargetType(String name) {