HHH-14905 - Verify that custom JavaType and JdbcType registration combo works

This commit is contained in:
Steve Ebersole 2021-11-05 10:35:21 -05:00
parent 9fec060fe2
commit 0eee5ff5b0
7 changed files with 112 additions and 13 deletions

View File

@ -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<String,BasicTypeImpl<?>> adHocBasicTypeRegistrations = new HashMap<>();
@Override
public void registerAdHocBasicType(BasicTypeImpl<?> basicType) {
adHocBasicTypeRegistrations.put( basicType.getName(), basicType );
}
@Override
public <T> BasicTypeImpl<T> resolveAdHocBasicType(String key) {
//noinspection unchecked
return (BasicTypeImpl<T>) adHocBasicTypeRegistrations.get( key );
}
@Override
public void release() {
classmateContext.release();

View File

@ -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
*/
<T> BasicTypeImpl<T> resolveAdHocBasicType(String key);
}

View File

@ -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<Object> 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) {

View File

@ -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 <J> BasicType<J> resolve(JavaType<J> jtdToUse, JdbcType stdToUse) {
return resolve(
jtdToUse,
stdToUse,
() -> new BasicTypeImpl<>( jtdToUse, stdToUse )
() -> {
final BasicTypeImpl<J> 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;
}
);
}

View File

@ -66,7 +66,7 @@ public class SerializableJavaTypeDescriptor<T extends Serializable> extends Abst
public JdbcType getRecommendedJdbcType(JdbcTypeDescriptorIndicators indicators) {
final int typeCode = indicators.isLob()
? Types.BLOB
: Types.LONGVARBINARY;
: Types.VARBINARY;
return indicators.getTypeConfiguration().getJdbcTypeDescriptorRegistry().getDescriptor( typeCode );
}

View File

@ -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<J> extends AbstractSingleColumnStandardBasicType<J> implements AdjustableBasicType<J> {
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<J> 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<J> extends AbstractSingleColumnStandardBasicType<J> 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<ol>
* <li>${u} is a unique number</li>
* <li>${o} is the mapped Java type</li>
* <li>${r} is the mapped SQL type (JDBC type code)</li>
* </ol>
*
* {@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;
}
}

View File

@ -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 <T> BasicTypeImpl<T> resolveAdHocBasicType(String key) {
return null;
}
@Override
public void release() {
delegate.release();