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.
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
`*jakarta.persistence.validation.factory*` (e.g. `jakarta.validation.ValidationFactory` implementation)::
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 preferredSqlTypeCodeForDuration;
private final int preferredSqlTypeCodeForUuid;
private final int preferredSqlTypeCodeForInstant;
private final TimeZoneStorageStrategy defaultTimeZoneStorageStrategy;
// Caching
@ -419,6 +420,7 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
this.preferredSqlTypeCodeForBoolean = ConfigurationHelper.getPreferredSqlTypeCodeForBoolean( serviceRegistry );
this.preferredSqlTypeCodeForDuration = ConfigurationHelper.getPreferredSqlTypeCodeForDuration( serviceRegistry );
this.preferredSqlTypeCodeForUuid = ConfigurationHelper.getPreferredSqlTypeCodeForUuid( serviceRegistry );
this.preferredSqlTypeCodeForInstant = ConfigurationHelper.getPreferredSqlTypeCodeForInstant( serviceRegistry );
this.defaultTimeZoneStorageStrategy = context.getMetadataBuildingOptions().getDefaultTimeZoneStorage();
final RegionFactory regionFactory = serviceRegistry.getService( RegionFactory.class );
@ -1202,6 +1204,11 @@ public class SessionFactoryOptionsBuilder implements SessionFactoryOptions {
return preferredSqlTypeCodeForUuid;
}
@Override
public int getPreferredSqlTypeCodeForInstant() {
return preferredSqlTypeCodeForInstant;
}
@Override
public TimeZoneStorageStrategy getDefaultTimeZoneStorageStrategy() {
return defaultTimeZoneStorageStrategy;

View File

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

View File

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

View File

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

View File

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

View File

@ -2514,6 +2514,20 @@ public interface AvailableSettings {
*/
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
* and deserialization, either:

View File

@ -226,6 +226,11 @@ public class BasicValueBinder implements JdbcTypeIndicators {
return buildingContext.getPreferredSqlTypeCodeForUuid();
}
@Override
public int getPreferredSqlTypeCodeForInstant() {
return buildingContext.getPreferredSqlTypeCodeForInstant();
}
@Override
public boolean 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> {
public static final TypeCodeConverter INSTANCE = new TypeCodeConverter();

View File

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

View File

@ -454,7 +454,7 @@ public final class StandardBasicTypes {
/**
* 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<>(
"instant",

View File

@ -68,7 +68,7 @@ public class InstantJavaType extends AbstractTemporalJavaType<Instant>
@Override
public JdbcType getRecommendedJdbcType(JdbcTypeIndicators context) {
return context.getTypeConfiguration().getJdbcTypeRegistry().getDescriptor( SqlTypes.TIMESTAMP_UTC );
return context.getTypeConfiguration().getJdbcTypeRegistry().getDescriptor( context.getPreferredSqlTypeCodeForInstant() );
}
@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/>
* Specifically names the key into the
* {@link JdbcTypeRegistry}.
@ -93,6 +93,16 @@ public interface JdbcTypeIndicators {
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)
*/

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`.
=== 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