HHH-15828 fix setting TIMEZONE_DEFAULT_STORAGE to COLUMN
This commit is contained in:
parent
4fba6ac60d
commit
ee66a93302
|
@ -746,10 +746,11 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
|
|||
|
||||
@Override
|
||||
public TimeZoneStorageStrategy getDefaultTimeZoneStorage() {
|
||||
return toTimeZoneStorageStrategy( getTimeZoneSupport( serviceRegistry ) );
|
||||
return toTimeZoneStorageStrategy( getTimeZoneSupport() );
|
||||
}
|
||||
|
||||
private static TimeZoneSupport getTimeZoneSupport(StandardServiceRegistry serviceRegistry) {
|
||||
@Override
|
||||
public TimeZoneSupport getTimeZoneSupport() {
|
||||
try {
|
||||
return serviceRegistry.getService( JdbcServices.class )
|
||||
.getDialect()
|
||||
|
@ -759,6 +760,7 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
|
|||
return TimeZoneSupport.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
private TimeZoneStorageStrategy toTimeZoneStorageStrategy(TimeZoneSupport timeZoneSupport) {
|
||||
switch ( defaultTimezoneStorage ) {
|
||||
case NATIVE:
|
||||
|
@ -775,9 +777,11 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
|
|||
case AUTO:
|
||||
switch (timeZoneSupport) {
|
||||
case NATIVE:
|
||||
// if the db has native support for timezones, we use that, not a column
|
||||
return TimeZoneStorageStrategy.NATIVE;
|
||||
case NORMALIZE:
|
||||
case NONE:
|
||||
// otherwise we use a separate column
|
||||
return TimeZoneStorageStrategy.COLUMN;
|
||||
default:
|
||||
throw new HibernateException( "Unsupported time zone support: " + timeZoneSupport);
|
||||
|
@ -785,9 +789,11 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
|
|||
case DEFAULT:
|
||||
switch (timeZoneSupport) {
|
||||
case NATIVE:
|
||||
// if the db has native support for timezones, we use that, and don't normalize
|
||||
return TimeZoneStorageStrategy.NATIVE;
|
||||
case NORMALIZE:
|
||||
case NONE:
|
||||
// otherwise we normalize things to UTC
|
||||
return TimeZoneStorageStrategy.NORMALIZE_UTC;
|
||||
default:
|
||||
throw new HibernateException( "Unsupported time zone support: " + timeZoneSupport);
|
||||
|
|
|
@ -66,7 +66,7 @@ import static org.hibernate.internal.util.config.ConfigurationHelper.getPreferre
|
|||
import static org.hibernate.internal.util.config.ConfigurationHelper.getPreferredSqlTypeCodeForUuid;
|
||||
|
||||
/**
|
||||
* Represents the process of of transforming a {@link MetadataSources}
|
||||
* Represents the process of transforming a {@link MetadataSources}
|
||||
* reference into a {@link org.hibernate.boot.Metadata} reference. Allows for 2 different process paradigms:<ul>
|
||||
* <li>
|
||||
* Single step : as defined by the {@link #build} method; internally leverages the 2-step paradigm
|
||||
|
@ -465,7 +465,7 @@ public class MetadataBuildingProcess {
|
|||
if ( timestampWithTimeZoneOverride != null ) {
|
||||
adaptToDefaultTimeZoneStorage( typeConfiguration, timestampWithTimeZoneOverride );
|
||||
}
|
||||
final int preferredSqlTypeCodeForInstant = getPreferredSqlTypeCodeForInstant(serviceRegistry);
|
||||
final int preferredSqlTypeCodeForInstant = getPreferredSqlTypeCodeForInstant( serviceRegistry );
|
||||
if ( preferredSqlTypeCodeForInstant != SqlTypes.TIMESTAMP_UTC ) {
|
||||
adaptToPreferredSqlTypeCodeForInstant( typeConfiguration, jdbcTypeRegistry, preferredSqlTypeCodeForInstant );
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
|
|||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||
import org.hibernate.cache.spi.access.AccessType;
|
||||
import org.hibernate.cfg.MetadataSourceType;
|
||||
import org.hibernate.dialect.TimeZoneSupport;
|
||||
import org.hibernate.id.factory.IdentifierGeneratorFactory;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
|
@ -58,6 +59,11 @@ public abstract class AbstractDelegatingMetadataBuildingOptions implements Metad
|
|||
return delegate.getDefaultTimeZoneStorage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeZoneSupport getTimeZoneSupport() {
|
||||
return delegate.getTimeZoneSupport();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BasicTypeRegistration> getBasicTypeRegistrations() {
|
||||
return delegate.getBasicTypeRegistrations();
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.hibernate.cache.spi.access.AccessType;
|
|||
import org.hibernate.cfg.MetadataSourceType;
|
||||
import org.hibernate.collection.internal.StandardCollectionSemanticsResolver;
|
||||
import org.hibernate.collection.spi.CollectionSemanticsResolver;
|
||||
import org.hibernate.dialect.TimeZoneSupport;
|
||||
import org.hibernate.id.factory.IdentifierGeneratorFactory;
|
||||
import org.hibernate.metamodel.internal.ManagedTypeRepresentationResolverStandard;
|
||||
import org.hibernate.metamodel.spi.ManagedTypeRepresentationResolver;
|
||||
|
@ -51,6 +52,8 @@ public interface MetadataBuildingOptions {
|
|||
|
||||
TimeZoneStorageStrategy getDefaultTimeZoneStorage();
|
||||
|
||||
TimeZoneSupport getTimeZoneSupport();
|
||||
|
||||
default ManagedTypeRepresentationResolver getManagedTypeRepresentationResolver() {
|
||||
// for now always return the standard one
|
||||
return ManagedTypeRepresentationResolverStandard.INSTANCE;
|
||||
|
|
|
@ -26,12 +26,9 @@ import jakarta.persistence.MappedSuperclass;
|
|||
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.Internal;
|
||||
import org.hibernate.TimeZoneStorageStrategy;
|
||||
import org.hibernate.annotations.ColumnTransformer;
|
||||
import org.hibernate.annotations.ColumnTransformers;
|
||||
import org.hibernate.annotations.TimeZoneColumn;
|
||||
import org.hibernate.annotations.TimeZoneStorage;
|
||||
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
|
||||
import org.hibernate.annotations.common.reflection.XClass;
|
||||
import org.hibernate.annotations.common.reflection.XProperty;
|
||||
|
@ -47,6 +44,8 @@ import org.hibernate.usertype.internal.AbstractTimeZoneStorageCompositeUserType;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.hibernate.cfg.AnnotationBinder.useColumnForTimeZoneStorage;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
|
@ -64,8 +63,8 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
private Map<String, JoinTable> currentPropertyJoinTableOverride;
|
||||
private Map<String, ForeignKey> holderForeignKeyOverride;
|
||||
private Map<String, ForeignKey> currentPropertyForeignKeyOverride;
|
||||
private String path;
|
||||
private MetadataBuildingContext context;
|
||||
private final String path;
|
||||
private final MetadataBuildingContext context;
|
||||
private Boolean isInIdClass;
|
||||
|
||||
AbstractPropertyHolder(
|
||||
|
@ -192,7 +191,7 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
this.currentPropertyColumnOverride = null;
|
||||
}
|
||||
|
||||
this.currentPropertyColumnTransformerOverride = buildColumnTransformerOverride( property, getPath() );
|
||||
this.currentPropertyColumnTransformerOverride = buildColumnTransformerOverride( property );
|
||||
if ( this.currentPropertyColumnTransformerOverride.size() == 0 ) {
|
||||
this.currentPropertyColumnTransformerOverride = null;
|
||||
}
|
||||
|
@ -217,7 +216,7 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
/**
|
||||
* Get column overriding, property first, then parent, then holder
|
||||
* replace the placeholder 'collection&&element' with nothing
|
||||
*
|
||||
* <p>
|
||||
* These rules are here to support both JPA 2 and legacy overriding rules.
|
||||
*/
|
||||
@Override
|
||||
|
@ -416,7 +415,7 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
|| current.isAnnotationPresent( Embeddable.class ) ) {
|
||||
//FIXME is embeddable override?
|
||||
Map<String, Column[]> currentOverride = buildColumnOverride( current, getPath(), context );
|
||||
Map<String, ColumnTransformer> currentTransformerOverride = buildColumnTransformerOverride( current, getPath() );
|
||||
Map<String, ColumnTransformer> currentTransformerOverride = buildColumnTransformerOverride( current );
|
||||
Map<String, JoinColumn[]> currentJoinOverride = buildJoinColumnOverride( current, getPath() );
|
||||
Map<String, JoinTable> currentJoinTableOverride = buildJoinTableOverride( current, getPath() );
|
||||
Map<String, ForeignKey> currentForeignKeyOverride = buildForeignKeyOverride( current, getPath() );
|
||||
|
@ -477,33 +476,65 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<String, List<Column>> entry : columnOverrideList.entrySet()) {
|
||||
for ( Map.Entry<String, List<Column>> entry : columnOverrideList.entrySet() ) {
|
||||
columnOverride.put( entry.getKey(), entry.getValue().toArray( new Column[0] ) );
|
||||
}
|
||||
}
|
||||
else if ( useColumnForTimeZoneStorage( element, context ) ) {
|
||||
final Column column = createTimestampColumn( element, path, context );
|
||||
columnOverride.put(
|
||||
entry.getKey(),
|
||||
entry.getValue().toArray( new Column[entry.getValue().size()] )
|
||||
path + "." + AbstractTimeZoneStorageCompositeUserType.INSTANT_NAME,
|
||||
new Column[]{ column }
|
||||
);
|
||||
final Column offsetColumn = createTimeZoneColumn( element, column );
|
||||
columnOverride.put(
|
||||
path + "." + AbstractTimeZoneStorageCompositeUserType.ZONE_OFFSET_NAME,
|
||||
new Column[]{ offsetColumn }
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
final TimeZoneStorage timeZoneStorage = element.getAnnotation( TimeZoneStorage.class );
|
||||
if ( timeZoneStorage != null ) {
|
||||
switch ( timeZoneStorage.value() ) {
|
||||
case AUTO:
|
||||
if ( context.getBuildingOptions().getDefaultTimeZoneStorage() != TimeZoneStorageStrategy.COLUMN ) {
|
||||
break;
|
||||
return columnOverride;
|
||||
}
|
||||
case COLUMN:
|
||||
final Column column;
|
||||
|
||||
private static Column createTimeZoneColumn(XAnnotatedElement element, Column column) {
|
||||
final TimeZoneColumn timeZoneColumn = element.getAnnotation( TimeZoneColumn.class );
|
||||
if ( timeZoneColumn != null ) {
|
||||
return new ColumnImpl(
|
||||
timeZoneColumn.name(),
|
||||
false,
|
||||
column.nullable(),
|
||||
timeZoneColumn.insertable(),
|
||||
timeZoneColumn.updatable(),
|
||||
timeZoneColumn.columnDefinition(),
|
||||
timeZoneColumn.table(),
|
||||
0
|
||||
);
|
||||
}
|
||||
else {
|
||||
return new ColumnImpl(
|
||||
column.name() + "_tz",
|
||||
false,
|
||||
column.nullable(),
|
||||
column.insertable(),
|
||||
column.updatable(),
|
||||
"",
|
||||
column.table(),
|
||||
0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static Column createTimestampColumn(XAnnotatedElement element, String path, MetadataBuildingContext context) {
|
||||
final Column annotatedColumn = element.getAnnotation( Column.class );
|
||||
if ( annotatedColumn != null ) {
|
||||
column = annotatedColumn;
|
||||
return annotatedColumn;
|
||||
}
|
||||
else {
|
||||
// Base the name of the synthetic dateTime field on the name of the original attribute
|
||||
final Identifier implicitName = context.getObjectNameNormalizer().normalizeIdentifierQuoting(
|
||||
context.getBuildingOptions().getImplicitNamingStrategy().determineBasicColumnName(
|
||||
new ImplicitBasicColumnNameSource() {
|
||||
final AttributePath attributePath = AttributePath.parse( path );
|
||||
final AttributePath attributePath = AttributePath.parse(path);
|
||||
|
||||
@Override
|
||||
public AttributePath getAttributePath() {
|
||||
|
@ -522,7 +553,7 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
}
|
||||
)
|
||||
);
|
||||
column = new ColumnImpl(
|
||||
return new ColumnImpl(
|
||||
implicitName.getText(),
|
||||
false,
|
||||
true,
|
||||
|
@ -533,49 +564,9 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
0
|
||||
);
|
||||
}
|
||||
columnOverride.put(
|
||||
path + "." + AbstractTimeZoneStorageCompositeUserType.INSTANT_NAME,
|
||||
new Column[] { column }
|
||||
);
|
||||
final Column offsetColumn;
|
||||
final TimeZoneColumn timeZoneColumn = element.getAnnotation( TimeZoneColumn.class );
|
||||
if ( timeZoneColumn != null ) {
|
||||
offsetColumn = new ColumnImpl(
|
||||
timeZoneColumn.name(),
|
||||
false,
|
||||
column.nullable(),
|
||||
timeZoneColumn.insertable(),
|
||||
timeZoneColumn.updatable(),
|
||||
timeZoneColumn.columnDefinition(),
|
||||
timeZoneColumn.table(),
|
||||
0
|
||||
);
|
||||
}
|
||||
else {
|
||||
offsetColumn = new ColumnImpl(
|
||||
column.name() + "_tz",
|
||||
false,
|
||||
column.nullable(),
|
||||
column.insertable(),
|
||||
column.updatable(),
|
||||
"",
|
||||
column.table(),
|
||||
0
|
||||
);
|
||||
}
|
||||
columnOverride.put(
|
||||
path + "." + AbstractTimeZoneStorageCompositeUserType.ZONE_OFFSET_NAME,
|
||||
new Column[] { offsetColumn }
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return columnOverride;
|
||||
}
|
||||
|
||||
private static Map<String, ColumnTransformer> buildColumnTransformerOverride(XAnnotatedElement element, String path) {
|
||||
private static Map<String, ColumnTransformer> buildColumnTransformerOverride(XAnnotatedElement element) {
|
||||
Map<String, ColumnTransformer> columnOverride = new HashMap<>();
|
||||
if ( element != null ) {
|
||||
ColumnTransformer singleOverride = element.getAnnotation( ColumnTransformer.class );
|
||||
|
|
|
@ -9,6 +9,8 @@ package org.hibernate.cfg;
|
|||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -74,6 +76,7 @@ import org.hibernate.cfg.annotations.EntityBinder;
|
|||
import org.hibernate.cfg.annotations.Nullability;
|
||||
import org.hibernate.cfg.annotations.PropertyBinder;
|
||||
import org.hibernate.cfg.annotations.QueryBinder;
|
||||
import org.hibernate.dialect.TimeZoneSupport;
|
||||
import org.hibernate.engine.OptimisticLockStyle;
|
||||
import org.hibernate.engine.spi.FilterDefinition;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
|
@ -171,6 +174,9 @@ import static org.hibernate.mapping.SimpleValue.DEFAULT_ID_GEN_STRATEGY;
|
|||
public final class AnnotationBinder {
|
||||
private static final CoreMessageLogger LOG = messageLogger( AnnotationBinder.class );
|
||||
|
||||
private static final String OFFSET_DATETIME_CLASS = OffsetDateTime.class.getName();
|
||||
private static final String ZONED_DATETIME_CLASS = ZonedDateTime.class.getName();
|
||||
|
||||
private AnnotationBinder() {}
|
||||
|
||||
public static void bindDefaults(MetadataBuildingContext context) {
|
||||
|
@ -1780,31 +1786,6 @@ public final class AnnotationBinder {
|
|||
return null;
|
||||
}
|
||||
|
||||
static Class<? extends CompositeUserType<?>> resolveTimeZoneStorageCompositeUserType(
|
||||
XProperty property,
|
||||
XClass returnedClass,
|
||||
MetadataBuildingContext context) {
|
||||
if ( property != null ) {
|
||||
final TimeZoneStorage timeZoneStorage = property.getAnnotation( TimeZoneStorage.class );
|
||||
if ( timeZoneStorage != null ) {
|
||||
switch ( timeZoneStorage.value() ) {
|
||||
case AUTO:
|
||||
if ( context.getBuildingOptions().getDefaultTimeZoneStorage() != TimeZoneStorageStrategy.COLUMN ) {
|
||||
return null;
|
||||
}
|
||||
case COLUMN:
|
||||
switch ( returnedClass.getName() ) {
|
||||
case "java.time.OffsetDateTime":
|
||||
return OffsetDateTimeCompositeUserType.class;
|
||||
case "java.time.ZonedDateTime":
|
||||
return ZonedDateTimeCompositeUserType.class;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isGlobalGeneratorNameGlobal(MetadataBuildingContext context) {
|
||||
return context.getBootstrapContext().getJpaCompliance().isGlobalGeneratorScopeEnabled();
|
||||
}
|
||||
|
@ -2495,4 +2476,51 @@ public final class AnnotationBinder {
|
|||
LOG.ignoreNotFoundWithFetchTypeLazy( entity, association );
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<? extends CompositeUserType<?>> resolveTimeZoneStorageCompositeUserType(
|
||||
XProperty property,
|
||||
XClass returnedClass,
|
||||
MetadataBuildingContext context) {
|
||||
if ( useColumnForTimeZoneStorage( property, context ) ) {
|
||||
String returnedClassName = returnedClass.getName();
|
||||
if ( OFFSET_DATETIME_CLASS.equals( returnedClassName ) ) {
|
||||
return OffsetDateTimeCompositeUserType.class;
|
||||
}
|
||||
else if ( ZONED_DATETIME_CLASS.equals( returnedClassName ) ) {
|
||||
return ZonedDateTimeCompositeUserType.class;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isZonedDateTimeClass(String returnedClassName) {
|
||||
return OFFSET_DATETIME_CLASS.equals( returnedClassName )
|
||||
|| ZONED_DATETIME_CLASS.equals( returnedClassName );
|
||||
}
|
||||
|
||||
static boolean useColumnForTimeZoneStorage(XAnnotatedElement element, MetadataBuildingContext context) {
|
||||
final TimeZoneStorage timeZoneStorage = element.getAnnotation( TimeZoneStorage.class );
|
||||
if ( timeZoneStorage == null ) {
|
||||
if ( element instanceof XProperty ) {
|
||||
XProperty property = (XProperty) element;
|
||||
return isZonedDateTimeClass( property.getType().getName() )
|
||||
//no @TimeZoneStorage annotation, so we need to use the default storage strategy
|
||||
&& context.getBuildingOptions().getDefaultTimeZoneStorage() == TimeZoneStorageStrategy.COLUMN;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch ( timeZoneStorage.value() ) {
|
||||
case COLUMN:
|
||||
return true;
|
||||
case AUTO:
|
||||
// if the db has native support for timezones, we use that, not a column
|
||||
return context.getBuildingOptions().getTimeZoneSupport() != TimeZoneSupport.NATIVE;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -930,9 +930,7 @@ public class BasicValueBinder implements JdbcTypeIndicators {
|
|||
|
||||
final Target targetAnn = findAnnotation( attributeXProperty, Target.class );
|
||||
if ( targetAnn != null ) {
|
||||
return (BasicJavaType<?>) typeConfiguration
|
||||
.getJavaTypeRegistry()
|
||||
.getDescriptor( targetAnn.value() );
|
||||
return (BasicJavaType<?>) typeConfiguration.getJavaTypeRegistry().getDescriptor( targetAnn.value() );
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -965,9 +963,6 @@ public class BasicValueBinder implements JdbcTypeIndicators {
|
|||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
timeZoneStorageType = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<? extends UserType<?>> normalizeUserType(Class<? extends UserType<?>> userType) {
|
||||
|
|
|
@ -13,7 +13,6 @@ import java.sql.SQLException;
|
|||
import java.sql.Timestamp;
|
||||
import java.sql.Types;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
|
|
Loading…
Reference in New Issue