Allow configuring the preferred JDBC type for Instant

This commit is contained in:
Christian Beikov 2022-03-29 17:35:36 +02:00
parent a9d1035806
commit 117e62195a
14 changed files with 94 additions and 3 deletions

View File

@ -392,6 +392,10 @@ Can also specify the name of the constant in `org.hibernate.type.SqlTypes` inste
Global setting identifying the preferred JDBC type code for storing duration values. Global setting identifying the preferred JDBC type code for storing duration values.
Can also specify the name of the constant in `org.hibernate.type.SqlTypes` instead. Can also specify the name of the constant in `org.hibernate.type.SqlTypes` instead.
`*hibernate.type.preferred_instant_jdbc_type*` (e.g. `93` for `java.sql.Types.TIMESTAMP` or `3003` for `org.hibernate.types.SqlTypes.TIMESTAMP_UTC` (default value))::
Global setting identifying the preferred JDBC type code for storing instant values.
Can also specify the name of the constant in `org.hibernate.type.SqlTypes` instead.
==== Bean Validation options ==== Bean Validation options
`*jakarta.persistence.validation.factory*` (e.g. `jakarta.validation.ValidationFactory` implementation):: `*jakarta.persistence.validation.factory*` (e.g. `jakarta.validation.ValidationFactory` implementation)::
Specify the `javax.validation.ValidationFactory` implementation to use for Bean Validation. Specify the `javax.validation.ValidationFactory` implementation to use for Bean Validation.

View File

@ -214,6 +214,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
private final int preferredSqlTypeCodeForBoolean; private final int preferredSqlTypeCodeForBoolean;
private final int preferredSqlTypeCodeForDuration; private final int preferredSqlTypeCodeForDuration;
private final int preferredSqlTypeCodeForUuid; private final int preferredSqlTypeCodeForUuid;
private final int preferredSqlTypeCodeForInstant;
private final TimeZoneStorageStrategy defaultTimeZoneStorageStrategy; private final TimeZoneStorageStrategy defaultTimeZoneStorageStrategy;
// Caching // Caching
@ -419,6 +420,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
this.preferredSqlTypeCodeForBoolean = ConfigurationHelper.getPreferredSqlTypeCodeForBoolean( serviceRegistry ); this.preferredSqlTypeCodeForBoolean = ConfigurationHelper.getPreferredSqlTypeCodeForBoolean( serviceRegistry );
this.preferredSqlTypeCodeForDuration = ConfigurationHelper.getPreferredSqlTypeCodeForDuration( serviceRegistry ); this.preferredSqlTypeCodeForDuration = ConfigurationHelper.getPreferredSqlTypeCodeForDuration( serviceRegistry );
this.preferredSqlTypeCodeForUuid = ConfigurationHelper.getPreferredSqlTypeCodeForUuid( serviceRegistry ); this.preferredSqlTypeCodeForUuid = ConfigurationHelper.getPreferredSqlTypeCodeForUuid( serviceRegistry );
this.preferredSqlTypeCodeForInstant = ConfigurationHelper.getPreferredSqlTypeCodeForInstant( serviceRegistry );
this.defaultTimeZoneStorageStrategy = context.getMetadataBuildingOptions().getDefaultTimeZoneStorage(); this.defaultTimeZoneStorageStrategy = context.getMetadataBuildingOptions().getDefaultTimeZoneStorage();
final RegionFactory regionFactory = serviceRegistry.getService( RegionFactory.class ); final RegionFactory regionFactory = serviceRegistry.getService( RegionFactory.class );
@ -1202,6 +1204,11 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
return preferredSqlTypeCodeForUuid; return preferredSqlTypeCodeForUuid;
} }
@Override
public int getPreferredSqlTypeCodeForInstant() {
return preferredSqlTypeCodeForInstant;
}
@Override @Override
public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() { public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() {
return defaultTimeZoneStorageStrategy; return defaultTimeZoneStorageStrategy;

View File

@ -7,6 +7,7 @@
package org.hibernate.boot.model.process.spi; package org.hibernate.boot.model.process.spi;
import java.sql.Types; import java.sql.Types;
import java.time.Instant;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.Collection; import java.util.Collection;
@ -448,6 +449,22 @@ public class MetadataBuildingProcess {
ZonedDateTime.class.getName() 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 void addFallbackIfNecessary( private static void addFallbackIfNecessary(

View File

@ -447,6 +447,11 @@ public class AbstractDelegatingSessionFactoryOptions implements SessionFactoryOp
return delegate.getPreferredSqlTypeCodeForUuid(); return delegate.getPreferredSqlTypeCodeForUuid();
} }
@Override
public int getPreferredSqlTypeCodeForInstant() {
return delegate.getPreferredSqlTypeCodeForInstant();
}
@Override @Override
public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() { public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() {
return delegate.getDefaultTimeZoneStorageStrategy(); return delegate.getDefaultTimeZoneStorageStrategy();

View File

@ -63,6 +63,10 @@ public interface MetadataBuildingContext {
return ConfigurationHelper.getPreferredSqlTypeCodeForUuid( getBootstrapContext().getServiceRegistry() ); return ConfigurationHelper.getPreferredSqlTypeCodeForUuid( getBootstrapContext().getServiceRegistry() );
} }
default int getPreferredSqlTypeCodeForInstant() {
return ConfigurationHelper.getPreferredSqlTypeCodeForInstant( getBootstrapContext().getServiceRegistry() );
}
TypeDefinitionRegistry getTypeDefinitionRegistry(); TypeDefinitionRegistry getTypeDefinitionRegistry();
/** /**

View File

@ -304,6 +304,8 @@ public interface SessionFactoryOptions extends QueryEngineOptions {
int getPreferredSqlTypeCodeForUuid(); int getPreferredSqlTypeCodeForUuid();
int getPreferredSqlTypeCodeForInstant();
TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy(); TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy();
FormatMapper getJsonFormatMapper(); FormatMapper getJsonFormatMapper();

View File

@ -2514,6 +2514,20 @@ public interface AvailableSettings {
*/ */
String PREFERRED_DURATION_JDBC_TYPE = "hibernate.type.preferred_duration_jdbc_type"; String PREFERRED_DURATION_JDBC_TYPE = "hibernate.type.preferred_duration_jdbc_type";
/**
* Specifies the preferred JDBC type for storing instant values. When no
* type is explicitly specified, {@link org.hibernate.type.SqlTypes#TIMESTAMP_UTC} is used.
*
* Can be overridden locally using {@link org.hibernate.annotations.JdbcType},
* {@link org.hibernate.annotations.JdbcTypeCode} and friends
*
* Can also specify the name of the constant in {@link org.hibernate.type.SqlTypes} instead. E.g.
* {@code hibernate.type.preferred_instant_jdbc_type=TIMESTAMP}
*
* @since 6.0
*/
String PREFERRED_INSTANT_JDBC_TYPE = "hibernate.type.preferred_instant_jdbc_type";
/** /**
* Specifies a {@link org.hibernate.type.FormatMapper} used for for JSON serialization * Specifies a {@link org.hibernate.type.FormatMapper} used for for JSON serialization
* and deserialization, either: * and deserialization, either:

View File

@ -226,6 +226,11 @@ public class BasicValueBinder implements JdbcTypeIndicators {
return buildingContext.getPreferredSqlTypeCodeForUuid(); return buildingContext.getPreferredSqlTypeCodeForUuid();
} }
@Override
public int getPreferredSqlTypeCodeForInstant() {
return buildingContext.getPreferredSqlTypeCodeForInstant();
}
@Override @Override
public boolean isNationalized() { public boolean isNationalized() {
return isNationalized; return isNationalized;

View File

@ -545,6 +545,14 @@ public final class ConfigurationHelper {
); );
} }
public static synchronized int getPreferredSqlTypeCodeForInstant(StandardServiceRegistry serviceRegistry) {
return serviceRegistry.getService( ConfigurationService.class ).getSetting(
AvailableSettings.PREFERRED_INSTANT_JDBC_TYPE,
TypeCodeConverter.INSTANCE,
SqlTypes.TIMESTAMP_UTC
);
}
private static class TypeCodeConverter implements ConfigurationService.Converter<Integer> { private static class TypeCodeConverter implements ConfigurationService.Converter<Integer> {
public static final TypeCodeConverter INSTANCE = new TypeCodeConverter(); public static final TypeCodeConverter INSTANCE = new TypeCodeConverter();

View File

@ -689,6 +689,11 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
return getBuildingContext().getPreferredSqlTypeCodeForUuid(); return getBuildingContext().getPreferredSqlTypeCodeForUuid();
} }
@Override
public int getPreferredSqlTypeCodeForInstant() {
return getBuildingContext().getPreferredSqlTypeCodeForInstant();
}
@Override @Override
public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() { public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() {
if ( timeZoneStorageType != null ) { if ( timeZoneStorageType != null ) {

View File

@ -454,7 +454,7 @@ public final class StandardBasicTypes {
/** /**
* The standard Hibernate type for mapping {@link Instant} to JDBC * The standard Hibernate type for mapping {@link Instant} to JDBC
* {@link org.hibernate.type.SqlTypes#TIMESTAMP TIMESTAMP}. * {@link org.hibernate.type.SqlTypes#TIMESTAMP_UTC TIMESTAMP_UTC}.
*/ */
public static final BasicTypeReference<Instant> INSTANT = new BasicTypeReference<>( public static final BasicTypeReference<Instant> INSTANT = new BasicTypeReference<>(
"instant", "instant",

View File

@ -68,7 +68,7 @@ public class InstantJavaType extends AbstractTemporalJavaType<Instant>
@Override @Override
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) { public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) {
return context.getTypeConfiguration().getJdbcTypeRegistry().getDescriptor( SqlTypes.TIMESTAMP_UTC ); return context.getTypeConfiguration().getJdbcTypeRegistry().getDescriptor( context.getPreferredSqlTypeCodeForInstant() );
} }
@Override @Override

View File

@ -84,7 +84,7 @@ public interface JdbcTypeIndicators {
} }
/** /**
* When mapping a uuid type to the database what is the preferred SQL type code to use? * When mapping an uuid type to the database what is the preferred SQL type code to use?
* <p/> * <p/>
* Specifically names the key into the * Specifically names the key into the
* {@link JdbcTypeRegistry}. * {@link JdbcTypeRegistry}.
@ -93,6 +93,16 @@ public interface JdbcTypeIndicators {
return SqlTypes.UUID; return SqlTypes.UUID;
} }
/**
* When mapping an instant type to the database what is the preferred SQL type code to use?
* <p/>
* Specifically names the key into the
* {@link JdbcTypeRegistry}.
*/
default int getPreferredSqlTypeCodeForInstant() {
return SqlTypes.TIMESTAMP_UTC;
}
/** /**
* Useful for resolutions based on column length. E.g. choosing between a VARCHAR (String) and a CHAR(1) (Character/char) * Useful for resolutions based on column length. E.g. choosing between a VARCHAR (String) and a CHAR(1) (Character/char)
*/ */

View File

@ -292,6 +292,16 @@ The migration to `uuid` might require a migration expression like `cast(old as u
To retain backwards compatibility, configure the setting `hibernate.type.preferred_uuid_jdbc_type` to `BINARY`. To retain backwards compatibility, configure the setting `hibernate.type.preferred_uuid_jdbc_type` to `BINARY`.
=== Instant mapping changes
Instant now maps to the type code `SqlType.TIMESTAMP_UTC` by default, which maps to the SQL type `timestamp with time zone`
if possible, and falls back to `timestamp`.
Due to this change, schema validation errors could occur on some databases.
The migration to `timestamp with time zone` might require a migration expression like `cast(old as timestamp with time zone)`.
To retain backwards compatibility, configure the setting `hibernate.type.preferred_instant_jdbc_type` to `TIMESTAMP`.
[[query]] [[query]]
== Query == Query