From 0eee5ff5b0ef094884979dec3a1ba3315af81069 Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 5 Nov 2021 10:35:21 -0500 Subject: [PATCH] HHH-14905 - Verify that custom JavaType and JdbcType registration combo works --- .../boot/internal/BootstrapContextImpl.java | 14 +++++++ .../hibernate/boot/spi/BootstrapContext.java | 11 +++++ .../org/hibernate/mapping/BasicValue.java | 27 +++++++++--- .../org/hibernate/type/BasicTypeRegistry.java | 20 +++++++-- .../java/SerializableJavaTypeDescriptor.java | 2 +- .../type/internal/BasicTypeImpl.java | 41 ++++++++++++++++++- .../testing/boot/BootstrapContextImpl.java | 10 +++++ 7 files changed, 112 insertions(+), 13 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/internal/BootstrapContextImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/internal/BootstrapContextImpl.java index 73fa0578bf..4f20d78490 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/internal/BootstrapContextImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/internal/BootstrapContextImpl.java @@ -39,6 +39,7 @@ import org.hibernate.metamodel.internal.ManagedTypeRepresentationResolverStandar import org.hibernate.metamodel.spi.ManagedTypeRepresentationResolver; import org.hibernate.query.sqm.function.SqmFunctionDescriptor; import org.hibernate.resource.beans.spi.BeanInstanceProducer; +import org.hibernate.type.internal.BasicTypeImpl; import org.hibernate.type.spi.TypeConfiguration; import org.jboss.jandex.IndexView; @@ -219,6 +220,19 @@ public class BootstrapContextImpl implements BootstrapContext { return cacheRegionDefinitions == null ? Collections.emptyList() : cacheRegionDefinitions; } + private final Map> adHocBasicTypeRegistrations = new HashMap<>(); + + @Override + public void registerAdHocBasicType(BasicTypeImpl basicType) { + adHocBasicTypeRegistrations.put( basicType.getName(), basicType ); + } + + @Override + public BasicTypeImpl resolveAdHocBasicType(String key) { + //noinspection unchecked + return (BasicTypeImpl) adHocBasicTypeRegistrations.get( key ); + } + @Override public void release() { classmateContext.release(); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/spi/BootstrapContext.java b/hibernate-core/src/main/java/org/hibernate/boot/spi/BootstrapContext.java index 473fa70927..446aa1c7ea 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/spi/BootstrapContext.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/spi/BootstrapContext.java @@ -26,6 +26,7 @@ import org.hibernate.jpa.spi.MutableJpaCompliance; import org.hibernate.metamodel.spi.ManagedTypeRepresentationResolver; import org.hibernate.query.sqm.function.SqmFunctionDescriptor; import org.hibernate.resource.beans.spi.BeanInstanceProducer; +import org.hibernate.type.internal.BasicTypeImpl; import org.hibernate.type.spi.TypeConfiguration; import org.jboss.jandex.IndexView; @@ -180,4 +181,14 @@ public interface BootstrapContext { * @todo verify this ^^ */ void release(); + + /** + * To support envers + */ + void registerAdHocBasicType(BasicTypeImpl basicType); + + /** + * To support envers + */ + BasicTypeImpl resolveAdHocBasicType(String key); } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java b/hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java index 2166877e00..a23e146293 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java @@ -25,6 +25,7 @@ import org.hibernate.boot.model.process.internal.NamedBasicTypeResolution; import org.hibernate.boot.model.process.internal.NamedConverterResolution; import org.hibernate.boot.model.process.internal.UserTypeResolution; import org.hibernate.boot.model.process.internal.VersionResolution; +import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.boot.spi.BootstrapContext; @@ -51,6 +52,7 @@ import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.MutabilityPlan; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptorIndicators; +import org.hibernate.type.internal.BasicTypeImpl; import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfigurationAware; import org.hibernate.usertype.DynamicParameterizedType; @@ -500,9 +502,8 @@ public class BasicValue extends SimpleValue implements JdbcTypeDescriptorIndicat TypeConfiguration typeConfiguration, MetadataBuildingContext context) { - final ManagedBeanRegistry managedBeanRegistry = context.getBootstrapContext() - .getServiceRegistry() - .getService( ManagedBeanRegistry.class ); + final StandardServiceRegistry serviceRegistry = context.getBootstrapContext().getServiceRegistry(); + final ManagedBeanRegistry managedBeanRegistry = serviceRegistry.getService( ManagedBeanRegistry.class ); final JpaAttributeConverterCreationContext converterCreationContext = new JpaAttributeConverterCreationContext() { @Override @@ -519,8 +520,10 @@ public class BasicValue extends SimpleValue implements JdbcTypeDescriptorIndicat // Name could refer to: // 1) a named converter - HBM support for JPA's AttributeConverter via its `type="..."` XML attribute - // 2) basic type "resolution key" - // 3) UserType or BasicType class name - directly, or through a TypeDefinition + // 2) a "named composed" mapping - like (1), this is mainly to support envers since it tells + // Hibernate the mappings via DOM. See `org.hibernate.type.internal.BasicTypeImpl` + // 3) basic type "resolution key" + // 4) UserType or BasicType class name - directly, or through a TypeDefinition if ( name.startsWith( ConverterDescriptor.TYPE_NAME_PREFIX ) ) { return NamedConverterResolution.from( @@ -534,6 +537,18 @@ public class BasicValue extends SimpleValue implements JdbcTypeDescriptorIndicat ); } + if ( name.startsWith( BasicTypeImpl.EXTERNALIZED_PREFIX ) ) { + final BasicTypeImpl basicType = context.getBootstrapContext().resolveAdHocBasicType( name ); + + return new NamedBasicTypeResolution( + basicType.getJavaTypeDescriptor(), + basicType, + null, + explicitMutabilityPlanAccess, + context + ); + } + // see if it is a named basic type final BasicType basicTypeByName = typeConfiguration.getBasicTypeRegistry().getRegisteredType( name ); if ( basicTypeByName != null ) { @@ -661,7 +676,7 @@ public class BasicValue extends SimpleValue implements JdbcTypeDescriptorIndicat } public void setExplicitTypeName(String typeName) { - this.explicitTypeName = typeName;; + this.explicitTypeName = typeName; } public void setTypeName(String typeName) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java b/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java index 4b2bc15525..4d4b749615 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java +++ b/hibernate-core/src/main/java/org/hibernate/type/BasicTypeRegistry.java @@ -19,11 +19,11 @@ import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.metamodel.model.convert.spi.BasicValueConverter; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.internal.BasicTypeImpl; import org.hibernate.type.internal.ConvertedBasicTypeImpl; import org.hibernate.type.internal.ImmutableConvertedBasicTypeImpl; import org.hibernate.type.internal.ImmutableNamedBasicTypeImpl; import org.hibernate.type.internal.NamedBasicTypeImpl; -import org.hibernate.type.internal.BasicTypeImpl; import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.usertype.UserType; @@ -152,14 +152,26 @@ public class BasicTypeRegistry implements Serializable { } /** - * Find an existing BasicType registration for the given JavaTypeDescriptor and - * SqlTypeDescriptor combo or create (and register) one. + * Find an existing BasicType registration for the given JavaType descriptor and + * JdbcType descriptor combo or create (and register) one. */ public BasicType resolve(JavaType jtdToUse, JdbcType stdToUse) { return resolve( jtdToUse, stdToUse, - () -> new BasicTypeImpl<>( jtdToUse, stdToUse ) + () -> { + final BasicTypeImpl basicType = new BasicTypeImpl<>( jtdToUse, stdToUse ); + + // if we are still building mappings, register this ad-hoc type via a + // unique code. this is to support envers + try { + typeConfiguration.getMetadataBuildingContext().getBootstrapContext().registerAdHocBasicType( basicType ); + } + catch (Exception ignore) { + } + + return basicType; + } ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/SerializableJavaTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/SerializableJavaTypeDescriptor.java index f966376611..74d5a00ca9 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/SerializableJavaTypeDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/SerializableJavaTypeDescriptor.java @@ -66,7 +66,7 @@ public class SerializableJavaTypeDescriptor extends Abst public JdbcType getRecommendedJdbcType(JdbcTypeDescriptorIndicators indicators) { final int typeCode = indicators.isLob() ? Types.BLOB - : Types.LONGVARBINARY; + : Types.VARBINARY; return indicators.getTypeConfiguration().getJdbcTypeDescriptorRegistry().getDescriptor( typeCode ); } diff --git a/hibernate-core/src/main/java/org/hibernate/type/internal/BasicTypeImpl.java b/hibernate-core/src/main/java/org/hibernate/type/internal/BasicTypeImpl.java index f5d7cb86da..973ea638b6 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/internal/BasicTypeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/type/internal/BasicTypeImpl.java @@ -6,6 +6,8 @@ */ package org.hibernate.type.internal; +import java.util.Locale; + import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.type.AbstractSingleColumnStandardBasicType; import org.hibernate.type.AdjustableBasicType; @@ -16,10 +18,23 @@ import org.hibernate.type.descriptor.jdbc.JdbcType; * @author Steve Ebersole */ public class BasicTypeImpl extends AbstractSingleColumnStandardBasicType implements AdjustableBasicType { + public static final String EXTERNALIZED_PREFIX = "basicType"; public static final String[] NO_REG_KEYS = ArrayHelper.EMPTY_STRING_ARRAY; + private static int count; + + private final String name; + public BasicTypeImpl(JavaType jtd, JdbcType std) { super( std, jtd ); + name = String.format( + Locale.ROOT, + "%s@%s(%s,%s)", + EXTERNALIZED_PREFIX, + ++count, + jtd.getJavaTypeClass().getName(), + std.getDefaultSqlTypeCode() + ); } @Override @@ -28,9 +43,31 @@ public class BasicTypeImpl extends AbstractSingleColumnStandardBasicType i return NO_REG_KEYS; } + /** + * BasicTypeImpl produces a name whose sole purpose is to + * be used as part of interpreting Envers-produced mappings. + * We want to use the same exact BasicTypeImpl *instance* in + * the audit mapping (Envers) as is used in the audited (ORM) + * mapping. + * + * The name is in the form {@code `basicType@${u}(${o},${r})`}, where
    + *
  1. ${u} is a unique number
  2. + *
  3. ${o} is the mapped Java type
  4. + *
  5. ${r} is the mapped SQL type (JDBC type code)
  6. + *
+ * + * {@code `basicType@${u}`} is enough to uniquely identify this type instance; + * the Java Type and JDBC type code are informational + * + * E.g. {@code `basicType@321(java.lang.String,12)`} + */ @Override public String getName() { - // again, irrelevant - return null; + return name; + } + + @Override + public String toString() { + return name; } } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/boot/BootstrapContextImpl.java b/hibernate-testing/src/main/java/org/hibernate/testing/boot/BootstrapContextImpl.java index 5d7ab8b071..6c81878a6a 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/boot/BootstrapContextImpl.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/boot/BootstrapContextImpl.java @@ -28,6 +28,7 @@ import org.hibernate.metamodel.internal.ManagedTypeRepresentationResolverStandar import org.hibernate.metamodel.spi.ManagedTypeRepresentationResolver; import org.hibernate.query.sqm.function.SqmFunctionDescriptor; import org.hibernate.resource.beans.spi.BeanInstanceProducer; +import org.hibernate.type.internal.BasicTypeImpl; import org.hibernate.type.spi.TypeConfiguration; import org.jboss.jandex.IndexView; @@ -151,6 +152,15 @@ public class BootstrapContextImpl implements BootstrapContext { return ManagedTypeRepresentationResolverStandard.INSTANCE; } + @Override + public void registerAdHocBasicType(BasicTypeImpl basicType) { + } + + @Override + public BasicTypeImpl resolveAdHocBasicType(String key) { + return null; + } + @Override public void release() { delegate.release();