HHH-15276 - Introduce ConverterRegistration

This commit is contained in:
Steve Ebersole 2022-05-20 11:22:47 -05:00
parent d73a5cde38
commit 571d46bef8
16 changed files with 478 additions and 167 deletions

View File

@ -0,0 +1,47 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.annotations;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
/**
* Registers an {@link jakarta.persistence.AttributeConverter}. The main
* purpose is to be able to control {@link jakarta.persistence.Converter#autoApply()}
* external to the actual converter class, which might be useful for third-parties
* converters.
*
* @author Steve Ebersole
*/
public @interface ConverterRegistration {
/**
* The converter class to register
*/
Class<? extends AttributeConverter<?,?>> converter();
/**
* The domain type to which this converter should be applied. This allows
* refining the domain type associated with the converter e.g. to apply to
* a subtype.
* <p/>
* With {@link #autoApply()} set to true, this will effectively override converters
* defined with {@link Converter#autoApply()} set to {@code false} and auto-apply them.
* <p/>
* With {@link #autoApply()} set to false, this will effectively override converters
* defined with {@link Converter#autoApply()} set to {@code true} and disable auto-apply
* for them.
*/
Class<?> domainType() default void.class;
/**
* Should the registered converter be auto applied for
* converting values of its reported domain type?
* <p/>
* Defaults to true as that is the more common use case for this annotation.
*/
boolean autoApply() default true;
}

View File

@ -473,7 +473,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
// attribute converters
@Override
public void addAttributeConverter(Class<? extends AttributeConverter> converterClass) {
public void addAttributeConverter(Class<? extends AttributeConverter<?,?>> converterClass) {
attributeConverterManager.addConverter(
new ClassBasedConverterDescriptor( converterClass, getBootstrapContext().getClassmateContext() )
);

View File

@ -7,10 +7,7 @@
package org.hibernate.boot.model.convert.internal;
import java.util.List;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import org.hibernate.AnnotationException;
import org.hibernate.boot.internal.ClassmateContext;
import org.hibernate.boot.model.convert.spi.AutoApplicableConverterDescriptor;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
@ -21,12 +18,16 @@ import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.type.descriptor.java.JavaType;
import com.fasterxml.classmate.ResolvedType;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import static org.hibernate.boot.model.convert.internal.ConverterHelper.resolveConverterClassParamTypes;
/**
* @author Steve Ebersole
*/
public abstract class AbstractConverterDescriptor implements ConverterDescriptor {
private final Class<? extends AttributeConverter> converterClass;
private final Class<? extends AttributeConverter<?,?>> converterClass;
private final ResolvedType domainType;
private final ResolvedType jdbcType;
@ -34,25 +35,12 @@ public abstract class AbstractConverterDescriptor implements ConverterDescriptor
private final AutoApplicableConverterDescriptor autoApplicableDescriptor;
public AbstractConverterDescriptor(
Class<? extends AttributeConverter> converterClass,
Class<? extends AttributeConverter<?,?>> converterClass,
Boolean forceAutoApply,
ClassmateContext classmateContext) {
this.converterClass = converterClass;
final ResolvedType converterType = classmateContext.getTypeResolver().resolve( converterClass );
final List<ResolvedType> converterParamTypes = converterType.typeParametersFor( AttributeConverter.class );
if ( converterParamTypes == null ) {
throw new AnnotationException(
"Could not extract type parameter information from AttributeConverter implementation ["
+ converterClass.getName() + "]"
);
}
else if ( converterParamTypes.size() != 2 ) {
throw new AnnotationException(
"Unexpected type parameter information for AttributeConverter implementation [" +
converterClass.getName() + "]; expected 2 parameter types, but found " + converterParamTypes.size()
);
}
final List<ResolvedType> converterParamTypes = resolveConverterClassParamTypes( converterClass, classmateContext );
this.domainType = converterParamTypes.get( 0 );
this.jdbcType = converterParamTypes.get( 1 );
@ -81,7 +69,7 @@ public abstract class AbstractConverterDescriptor implements ConverterDescriptor
}
@Override
public Class<? extends AttributeConverter> getAttributeConverterClass() {
public Class<? extends AttributeConverter<?,?>> getAttributeConverterClass() {
return converterClass;
}

View File

@ -15,25 +15,58 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.internal.ClassmateContext;
import org.hibernate.boot.model.convert.spi.AutoApplicableConverterDescriptor;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.model.convert.spi.ConverterAutoApplyHandler;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.model.convert.spi.RegisteredConversion;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.internal.util.StringHelper;
import org.jboss.logging.Logger;
import com.fasterxml.classmate.ResolvedType;
import jakarta.persistence.AttributeConverter;
/**
* @implNote It is important that all {@link RegisteredConversion} be registered
* prior to attempts to register any {@link ConverterDescriptor}
*
* @author Steve Ebersole
*/
public class AttributeConverterManager implements ConverterAutoApplyHandler {
private static final Logger log = Logger.getLogger( AttributeConverterManager.class );
private Map<Class, ConverterDescriptor> attributeConverterDescriptorsByClass;
private Map<Class<?>, ConverterDescriptor> attributeConverterDescriptorsByClass;
private Map<Class<?>, RegisteredConversion> registeredConversionsByDomainType;
public void addConverter(ConverterDescriptor descriptor) {
if ( log.isTraceEnabled() ) {
log.tracef( "Starting AttributeConverterManager#addConverter : `%s`", descriptor.getAttributeConverterClass().getName() );
}
if ( registeredConversionsByDomainType != null ) {
final ResolvedType domainValueResolvedType = descriptor.getDomainValueResolvedType();
final Class<?> domainType = domainValueResolvedType.getErasedType();
final RegisteredConversion registeredConversion = registeredConversionsByDomainType.get( domainType );
if ( registeredConversion != null ) {
// we can skip the registering the converter...
// the RegisteredConversion will always take precedence
if ( log.isDebugEnabled() ) {
log.debugf(
"Skipping registration of discovered AttributeConverter `%s` for auto-apply; there was" +
"already ",
descriptor.getAttributeConverterClass().getName()
);
}
return;
}
}
if ( attributeConverterDescriptorsByClass == null ) {
attributeConverterDescriptorsByClass = new ConcurrentHashMap<>();
}
@ -54,6 +87,49 @@ public class AttributeConverterManager implements ConverterAutoApplyHandler {
}
}
public void addRegistration(RegisteredConversion conversion, BootstrapContext context) {
if ( registeredConversionsByDomainType == null ) {
registeredConversionsByDomainType = new ConcurrentHashMap<>();
}
final Class<?> domainType;
if ( conversion.getExplicitDomainType().equals( void.class ) ) {
// the registration did not define an explicit domain-type, so inspect the converter
final ClassmateContext classmateContext = context.getClassmateContext();
final ResolvedType converterType = classmateContext.getTypeResolver().resolve( conversion.getConverterType() );
final List<ResolvedType> converterParamTypes = converterType.typeParametersFor( AttributeConverter.class );
if ( converterParamTypes == null ) {
throw new AnnotationException(
"Could not extract type parameter information from AttributeConverter implementation ["
+ conversion.getConverterType().getName() + "]"
);
}
else if ( converterParamTypes.size() != 2 ) {
throw new AnnotationException(
"Unexpected type parameter information for AttributeConverter implementation [" +
conversion.getConverterType().getName() + "]; expected 2 parameter types, but found " + converterParamTypes.size()
);
}
domainType = converterParamTypes.get( 0 ).getErasedType();
}
else {
domainType = conversion.getExplicitDomainType();
}
// see if we have a matching entry in `attributeConverterDescriptorsByClass`.
// if so, remove it. The conversion being registered will always take precedence
final ConverterDescriptor removed = attributeConverterDescriptorsByClass.remove( conversion.getConverterType() );
if ( removed != null && log.isDebugEnabled() ) {
log.debugf(
"Removed potentially auto-applicable converter `%s` due to @ConverterRegistration",
removed.getAttributeConverterClass().getName()
);
}
registeredConversionsByDomainType.put( domainType, conversion );
}
private Collection<ConverterDescriptor> converterDescriptors() {
if ( attributeConverterDescriptorsByClass == null ) {
return Collections.emptyList();
@ -83,17 +159,33 @@ public class AttributeConverterManager implements ConverterAutoApplyHandler {
return locateMatchingConverter(
property,
ConversionSite.ATTRIBUTE,
(autoApplyDescriptor) -> autoApplyDescriptor.getAutoAppliedConverterDescriptorForAttribute( property, context )
(autoApplyDescriptor) -> autoApplyDescriptor.getAutoAppliedConverterDescriptorForAttribute( property, context ),
context
);
}
private static StringHelper.Renderer<ConverterDescriptor> RENDERER = value -> value.getAttributeConverterClass().getName();
private static final StringHelper.Renderer<ConverterDescriptor> RENDERER = value -> value.getAttributeConverterClass().getName();
private ConverterDescriptor locateMatchingConverter(
XProperty xProperty,
ConversionSite conversionSite,
Function<AutoApplicableConverterDescriptor, ConverterDescriptor> matcher) {
List<ConverterDescriptor> matches = new ArrayList<>();
Function<AutoApplicableConverterDescriptor, ConverterDescriptor> matcher,
MetadataBuildingContext context) {
if ( registeredConversionsByDomainType != null ) {
// we had registered conversions - see if any of them match and, if so, use that conversion
final ResolvedType resolveAttributeType = ConverterHelper.resolveAttributeType( xProperty, context );
final RegisteredConversion registrationForDomainType = registeredConversionsByDomainType.get( resolveAttributeType.getErasedType() );
if ( registrationForDomainType != null ) {
if ( registrationForDomainType.isAutoApply() ) {
return registrationForDomainType.getConverterDescriptor();
}
else {
return null;
}
}
}
final List<ConverterDescriptor> matches = new ArrayList<>();
for ( ConverterDescriptor descriptor : converterDescriptors() ) {
if ( log.isDebugEnabled() ) {
@ -143,7 +235,8 @@ public class AttributeConverterManager implements ConverterAutoApplyHandler {
return locateMatchingConverter(
property,
ConversionSite.COLLECTION_ELEMENT,
(autoApplyDescriptor) -> autoApplyDescriptor.getAutoAppliedConverterDescriptorForCollectionElement( property, context )
(autoApplyDescriptor) -> autoApplyDescriptor.getAutoAppliedConverterDescriptorForCollectionElement( property, context ),
context
);
}
@ -154,7 +247,8 @@ public class AttributeConverterManager implements ConverterAutoApplyHandler {
return locateMatchingConverter(
property,
ConversionSite.MAP_KEY,
(autoApplyDescriptor) -> autoApplyDescriptor.getAutoAppliedConverterDescriptorForMapKey( property, context )
(autoApplyDescriptor) -> autoApplyDescriptor.getAutoAppliedConverterDescriptorForMapKey( property, context ),
context
);
}

View File

@ -6,27 +6,17 @@
*/
package org.hibernate.boot.model.convert.internal;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.internal.ClassmateContext;
import org.hibernate.boot.model.convert.spi.AutoApplicableConverterDescriptor;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.annotations.HCANNHelper;
import org.hibernate.internal.util.type.PrimitiveWrapperHelper;
import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.ResolvedTypeWithMembers;
import com.fasterxml.classmate.members.ResolvedField;
import com.fasterxml.classmate.members.ResolvedMember;
import com.fasterxml.classmate.members.ResolvedMethod;
/**
* Standard implementation of AutoApplicableConverterDescriptor
@ -44,9 +34,9 @@ public class AutoApplicableConverterDescriptorStandardImpl implements AutoApplic
public ConverterDescriptor getAutoAppliedConverterDescriptorForAttribute(
XProperty xProperty,
MetadataBuildingContext context) {
final ResolvedType attributeType = resolveAttributeType( xProperty, context );
final ResolvedType attributeType = ConverterHelper.resolveAttributeType( xProperty, context );
return typesMatch( linkedConverterDescriptor.getDomainValueResolvedType(), attributeType )
return ConverterHelper.typesMatch( linkedConverterDescriptor.getDomainValueResolvedType(), attributeType )
? linkedConverterDescriptor
: null;
}
@ -55,7 +45,7 @@ public class AutoApplicableConverterDescriptorStandardImpl implements AutoApplic
public ConverterDescriptor getAutoAppliedConverterDescriptorForCollectionElement(
XProperty xProperty,
MetadataBuildingContext context) {
final ResolvedMember collectionMember = resolveMember( xProperty, context );
final ResolvedMember<?> collectionMember = ConverterHelper.resolveMember( xProperty, context );
final ResolvedType elementType;
if ( Map.class.isAssignableFrom( collectionMember.getType().getErasedType() ) ) {
@ -68,7 +58,7 @@ public class AutoApplicableConverterDescriptorStandardImpl implements AutoApplic
throw new HibernateException( "Attribute was neither a Collection nor a Map : " + collectionMember.getType().getErasedType() );
}
return typesMatch( linkedConverterDescriptor.getDomainValueResolvedType(), elementType )
return ConverterHelper.typesMatch( linkedConverterDescriptor.getDomainValueResolvedType(), elementType )
? linkedConverterDescriptor
: null;
}
@ -78,7 +68,7 @@ public class AutoApplicableConverterDescriptorStandardImpl implements AutoApplic
XProperty xProperty,
MetadataBuildingContext context) {
final ResolvedMember collectionMember = resolveMember( xProperty, context );
final ResolvedMember<?> collectionMember = ConverterHelper.resolveMember( xProperty, context );
final ResolvedType keyType;
if ( Map.class.isAssignableFrom( collectionMember.getType().getErasedType() ) ) {
@ -88,102 +78,10 @@ public class AutoApplicableConverterDescriptorStandardImpl implements AutoApplic
throw new HibernateException( "Attribute was not a Map : " + collectionMember.getType().getErasedType() );
}
return typesMatch( linkedConverterDescriptor.getDomainValueResolvedType(), keyType )
return ConverterHelper.typesMatch( linkedConverterDescriptor.getDomainValueResolvedType(), keyType )
? linkedConverterDescriptor
: null;
}
private ResolvedType resolveAttributeType(XProperty xProperty, MetadataBuildingContext context) {
return resolveMember( xProperty, context ).getType();
}
private ResolvedMember resolveMember(XProperty xProperty, MetadataBuildingContext buildingContext) {
final ClassmateContext classmateContext = buildingContext.getBootstrapContext().getClassmateContext();
final ReflectionManager reflectionManager = buildingContext.getBootstrapContext().getReflectionManager();
final ResolvedType declaringClassType = classmateContext.getTypeResolver().resolve(
reflectionManager.toClass( xProperty.getDeclaringClass() )
);
final ResolvedTypeWithMembers declaringClassWithMembers = classmateContext.getMemberResolver().resolve(
declaringClassType,
null,
null
);
final Member member = toMember( xProperty );
if ( member instanceof Method ) {
for ( ResolvedMethod resolvedMember : declaringClassWithMembers.getMemberMethods() ) {
if ( resolvedMember.getName().equals( member.getName() ) ) {
return resolvedMember;
}
}
}
else if ( member instanceof Field ) {
for ( ResolvedField resolvedMember : declaringClassWithMembers.getMemberFields() ) {
if ( resolvedMember.getName().equals( member.getName() ) ) {
return resolvedMember;
}
}
}
else {
throw new HibernateException( "Unexpected java.lang.reflect.Member type from org.hibernate.annotations.common.reflection.java.JavaXMember : " + member );
}
throw new HibernateException(
"Could not locate resolved type information for attribute [" + member.getName() + "] from Classmate"
);
}
private static Member toMember(XProperty xProperty) {
try {
return HCANNHelper.getUnderlyingMember( xProperty );
}
catch (Exception e) {
throw new HibernateException(
"Could not resolve member signature from XProperty reference",
e
);
}
}
private boolean typesMatch(ResolvedType converterDefinedType, ResolvedType checkType) {
Class<?> erasedCheckType = checkType.getErasedType();
if ( erasedCheckType.isPrimitive() ) {
erasedCheckType = PrimitiveWrapperHelper.getDescriptorByPrimitiveType( erasedCheckType ).getWrapperClass();
}
if ( !converterDefinedType.getErasedType().isAssignableFrom( erasedCheckType ) ) {
return false;
}
// if the converter did not define any nested type parameters, then the check above is
// enough for a match
if ( converterDefinedType.getTypeParameters().isEmpty() ) {
return true;
}
// however, here the converter *did* define nested type parameters, so we'd have a converter defined using something like, e.g., List<String> for its
// domain type.
//
// we need to check those nested types as well
if ( checkType.getTypeParameters().isEmpty() ) {
// the domain type did not define nested type params. a List<String> would not auto-match a List(<Object>)
return false;
}
if ( converterDefinedType.getTypeParameters().size() != checkType.getTypeParameters().size() ) {
// they had different number of type params somehow.
return false;
}
for ( int i = 0; i < converterDefinedType.getTypeParameters().size(); i++ ) {
if ( !typesMatch( converterDefinedType.getTypeParameters().get( i ), checkType.getTypeParameters().get( i ) ) ) {
return false;
}
}
return true;
}
}

View File

@ -6,12 +6,12 @@
*/
package org.hibernate.boot.model.convert.internal;
import jakarta.persistence.AttributeConverter;
import org.hibernate.boot.internal.ClassmateContext;
import org.hibernate.boot.model.convert.spi.JpaAttributeConverterCreationContext;
import org.hibernate.resource.beans.spi.ManagedBean;
import jakarta.persistence.AttributeConverter;
/**
* ConverterDescriptor implementation for cases where we know the
* AttributeConverter Class. This is the normal case.
@ -20,13 +20,13 @@ import org.hibernate.resource.beans.spi.ManagedBean;
*/
public class ClassBasedConverterDescriptor extends AbstractConverterDescriptor {
public ClassBasedConverterDescriptor(
Class<? extends AttributeConverter> converterClass,
Class<? extends AttributeConverter<?,?>> converterClass,
ClassmateContext classmateContext) {
super( converterClass, null, classmateContext );
}
public ClassBasedConverterDescriptor(
Class<? extends AttributeConverter> converterClass,
Class<? extends AttributeConverter<?,?>> converterClass,
Boolean forceAutoApply,
ClassmateContext classmateContext) {
super( converterClass, forceAutoApply, classmateContext );

View File

@ -6,10 +6,27 @@
*/
package org.hibernate.boot.model.convert.internal;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import org.hibernate.AnnotationException;
import org.hibernate.HibernateException;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.internal.ClassmateContext;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.annotations.HCANNHelper;
import org.hibernate.internal.util.GenericsHelper;
import org.hibernate.internal.util.type.PrimitiveWrapperHelper;
import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.ResolvedTypeWithMembers;
import com.fasterxml.classmate.members.ResolvedField;
import com.fasterxml.classmate.members.ResolvedMember;
import com.fasterxml.classmate.members.ResolvedMethod;
import jakarta.persistence.AttributeConverter;
/**
@ -19,4 +36,125 @@ public class ConverterHelper {
public static ParameterizedType extractAttributeConverterParameterizedType(Class<? extends AttributeConverter<?,?>> base) {
return GenericsHelper.extractParameterizedType( base );
}
public static ResolvedType resolveAttributeType(XProperty xProperty, MetadataBuildingContext context) {
return resolveMember( xProperty, context ).getType();
}
public static ResolvedMember<? extends Member> resolveMember(XProperty xProperty, MetadataBuildingContext buildingContext) {
final ClassmateContext classmateContext = buildingContext.getBootstrapContext().getClassmateContext();
final ReflectionManager reflectionManager = buildingContext.getBootstrapContext().getReflectionManager();
final ResolvedType declaringClassType = classmateContext.getTypeResolver().resolve(
reflectionManager.toClass( xProperty.getDeclaringClass() )
);
final ResolvedTypeWithMembers declaringClassWithMembers = classmateContext.getMemberResolver().resolve(
declaringClassType,
null,
null
);
final Member member = toMember( xProperty );
if ( member instanceof Method ) {
for ( ResolvedMethod resolvedMember : declaringClassWithMembers.getMemberMethods() ) {
if ( resolvedMember.getName().equals( member.getName() ) ) {
return resolvedMember;
}
}
}
else if ( member instanceof Field ) {
for ( ResolvedField resolvedMember : declaringClassWithMembers.getMemberFields() ) {
if ( resolvedMember.getName().equals( member.getName() ) ) {
return resolvedMember;
}
}
}
else {
throw new HibernateException( "Unexpected java.lang.reflect.Member type from org.hibernate.annotations.common.reflection.java.JavaXMember : " + member );
}
throw new HibernateException(
"Could not locate resolved type information for attribute [" + member.getName() + "] from Classmate"
);
}
public static Member toMember(XProperty xProperty) {
try {
return HCANNHelper.getUnderlyingMember( xProperty );
}
catch (Exception e) {
throw new HibernateException(
"Could not resolve member signature from XProperty reference",
e
);
}
}
public static List<ResolvedType> resolveConverterClassParamTypes(
Class<? extends AttributeConverter<?, ?>> converterClass,
ClassmateContext context) {
final ResolvedType converterType = context.getTypeResolver().resolve( converterClass );
final List<ResolvedType> converterParamTypes = converterType.typeParametersFor( AttributeConverter.class );
if ( converterParamTypes == null ) {
throw new AnnotationException(
"Could not extract type parameter information from AttributeConverter implementation ["
+ converterClass.getName() + "]"
);
}
else if ( converterParamTypes.size() != 2 ) {
throw new AnnotationException(
"Unexpected type parameter information for AttributeConverter implementation [" +
converterClass.getName() + "]; expected 2 parameter types, but found " + converterParamTypes.size()
);
}
return converterParamTypes;
}
/**
* Determine whether 2 types match. Intended for determining whether to auto applying a converter
*
* @param converterDefinedType The type defined via the converter's parameterized type signature.
* E.g. {@code O} in {@code implements AttributeConverter<O,R>}
* @param checkType The type from the domain model (basic attribute type, Map key type, Collection element type)
*
* @return {@code true} if they match, otherwise {@code false}.
*/
public static boolean typesMatch(ResolvedType converterDefinedType, ResolvedType checkType) {
Class<?> erasedCheckType = checkType.getErasedType();
if ( erasedCheckType.isPrimitive() ) {
erasedCheckType = PrimitiveWrapperHelper.getDescriptorByPrimitiveType( erasedCheckType ).getWrapperClass();
}
if ( !converterDefinedType.getErasedType().isAssignableFrom( erasedCheckType ) ) {
return false;
}
// if the converter did not define any nested type parameters, then the check above is
// enough for a match
if ( converterDefinedType.getTypeParameters().isEmpty() ) {
return true;
}
// however, here the converter *did* define nested type parameters, so we'd have a converter defined using something like, e.g., List<String> for its
// domain type.
//
// we need to check those nested types as well
if ( checkType.getTypeParameters().isEmpty() ) {
// the domain type did not define nested type params. a List<String> would not auto-match a List(<Object>)
return false;
}
if ( converterDefinedType.getTypeParameters().size() != checkType.getTypeParameters().size() ) {
// they had different number of type params somehow.
return false;
}
for ( int i = 0; i < converterDefinedType.getTypeParameters().size(); i++ ) {
if ( !typesMatch( converterDefinedType.getTypeParameters().get( i ), checkType.getTypeParameters().get( i ) ) ) {
return false;
}
}
return true;
}
}

View File

@ -6,13 +6,13 @@
*/
package org.hibernate.boot.model.convert.internal;
import jakarta.persistence.AttributeConverter;
import org.hibernate.boot.internal.ClassmateContext;
import org.hibernate.boot.model.convert.spi.JpaAttributeConverterCreationContext;
import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.resource.beans.spi.ProvidedInstanceManagedBeanImpl;
import jakarta.persistence.AttributeConverter;
/**
* ConverterDescriptor implementation for cases where we are handed
* the AttributeConverter instance to use.
@ -28,11 +28,12 @@ public class InstanceBasedConverterDescriptor extends AbstractConverterDescripto
this( converterInstance, null, classmateContext );
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public InstanceBasedConverterDescriptor(
AttributeConverter<?,?> converterInstance,
Boolean forceAutoApply,
ClassmateContext classmateContext) {
super( converterInstance.getClass(), forceAutoApply, classmateContext );
super( (Class) converterInstance.getClass(), forceAutoApply, classmateContext );
this.converterInstance = converterInstance;
}

View File

@ -6,11 +6,10 @@
*/
package org.hibernate.boot.model.convert.spi;
import jakarta.persistence.AttributeConverter;
import org.hibernate.metamodel.model.convert.spi.JpaAttributeConverter;
import com.fasterxml.classmate.ResolvedType;
import jakarta.persistence.AttributeConverter;
/**
* Boot-time descriptor of a JPA AttributeConverter
@ -23,7 +22,7 @@ public interface ConverterDescriptor {
/**
* The AttributeConverter class
*/
Class<? extends AttributeConverter> getAttributeConverterClass();
Class<? extends AttributeConverter<?,?>> getAttributeConverterClass();
/**
* The resolved Classmate type descriptor for the conversion's domain type

View File

@ -0,0 +1,151 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.boot.model.convert.spi;
import java.util.List;
import org.hibernate.boot.model.convert.internal.AutoApplicableConverterDescriptorBypassedImpl;
import org.hibernate.boot.model.convert.internal.AutoApplicableConverterDescriptorStandardImpl;
import org.hibernate.boot.model.convert.internal.ConverterHelper;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.metamodel.model.convert.internal.JpaAttributeConverterImpl;
import org.hibernate.metamodel.model.convert.spi.JpaAttributeConverter;
import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
import org.hibernate.type.spi.TypeConfiguration;
import com.fasterxml.classmate.ResolvedType;
import jakarta.persistence.AttributeConverter;
/**
* A registered conversion.
*
* @see org.hibernate.annotations.ConverterRegistration
*
* @author Steve Ebersole
*/
public class RegisteredConversion {
private final Class<?> explicitDomainType;
private final Class<? extends AttributeConverter<?,?>> converterType;
private final boolean autoApply;
private final ConverterDescriptor converterDescriptor;
public RegisteredConversion(
Class<?> explicitDomainType,
Class<? extends AttributeConverter<?,?>> converterType,
boolean autoApply,
MetadataBuildingContext context) {
this.explicitDomainType = explicitDomainType;
this.converterType = converterType;
this.autoApply = autoApply;
this.converterDescriptor = determineConverterDescriptor( explicitDomainType, converterType, autoApply, context );
}
private static ConverterDescriptor determineConverterDescriptor(
Class<?> explicitDomainType,
Class<? extends AttributeConverter<?, ?>> converterType,
boolean autoApply,
MetadataBuildingContext context) {
final List<ResolvedType> resolvedParamTypes = ConverterHelper.resolveConverterClassParamTypes(
converterType,
context.getBootstrapContext().getClassmateContext()
);
final ResolvedType relationalType = resolvedParamTypes.get( 1 );
final ResolvedType domainTypeToMatch;
if ( explicitDomainType != null ) {
domainTypeToMatch = context.getBootstrapContext().getClassmateContext().getTypeResolver().resolve( explicitDomainType );
}
else {
domainTypeToMatch = resolvedParamTypes.get( 0 );
}
return new ConverterDescriptorImpl( converterType, domainTypeToMatch, relationalType, autoApply );
}
public Class<?> getExplicitDomainType() {
return explicitDomainType;
}
public Class<?> getConverterType() {
return converterType;
}
public boolean isAutoApply() {
return autoApply;
}
public ConverterDescriptor getConverterDescriptor() {
return converterDescriptor;
}
private static class ConverterDescriptorImpl implements ConverterDescriptor {
private final Class<? extends AttributeConverter<?, ?>> converterType;
private final ResolvedType domainTypeToMatch;
private final ResolvedType relationalType;
private final boolean autoApply;
private final AutoApplicableConverterDescriptor autoApplyDescriptor;
public ConverterDescriptorImpl(
Class<? extends AttributeConverter<?, ?>> converterType,
ResolvedType domainTypeToMatch,
ResolvedType relationalType,
boolean autoApply) {
this.converterType = converterType;
this.domainTypeToMatch = domainTypeToMatch;
this.relationalType = relationalType;
this.autoApply = autoApply;
this.autoApplyDescriptor = autoApply
? new AutoApplicableConverterDescriptorStandardImpl( this )
: AutoApplicableConverterDescriptorBypassedImpl.INSTANCE;
}
@Override
public Class<? extends AttributeConverter<?, ?>> getAttributeConverterClass() {
return converterType;
}
@Override
public ResolvedType getDomainValueResolvedType() {
return domainTypeToMatch;
}
@Override
public ResolvedType getRelationalValueResolvedType() {
return relationalType;
}
@Override
public AutoApplicableConverterDescriptor getAutoApplyDescriptor() {
return autoApplyDescriptor;
}
@SuppressWarnings("unchecked")
@Override
public JpaAttributeConverter<?, ?> createJpaAttributeConverter(JpaAttributeConverterCreationContext context) {
final ManagedBean<? extends AttributeConverter<?, ?>> converterBean = context
.getManagedBeanRegistry()
.getBean( converterType );
final TypeConfiguration typeConfiguration = context.getTypeConfiguration();
final JavaTypeRegistry javaTypeRegistry = typeConfiguration.getJavaTypeRegistry();
javaTypeRegistry.resolveDescriptor( domainTypeToMatch.getErasedType() );
//noinspection rawtypes
return new JpaAttributeConverterImpl(
converterBean,
javaTypeRegistry.getDescriptor( converterBean.getBeanClass() ),
javaTypeRegistry.resolveDescriptor( domainTypeToMatch.getErasedType() ),
javaTypeRegistry.resolveDescriptor( relationalType.getErasedType() )
);
}
}
}

View File

@ -50,7 +50,7 @@ public class AnnotationMetadataSourceProcessorImpl implements MetadataSourceProc
private final MetadataBuildingContextRootImpl rootMetadataBuildingContext;
@SuppressWarnings("FieldCanBeLocal")
@SuppressWarnings({ "FieldCanBeLocal", "unused" })
private final IndexView jandexView;
private final ReflectionManager reflectionManager;
@ -112,8 +112,8 @@ public class AnnotationMetadataSourceProcessorImpl implements MetadataSourceProc
final XClass xClass = reflectionManager.toXClass( annotatedClass );
// categorize it, based on assumption it does not fall into multiple categories
if ( xClass.isAnnotationPresent( Converter.class ) ) {
//noinspection unchecked, rawtypes
attributeConverterManager.addAttributeConverter( (Class<? extends AttributeConverter>) annotatedClass );
//noinspection unchecked
attributeConverterManager.addAttributeConverter( (Class<? extends AttributeConverter<?,?>>) annotatedClass );
}
else if ( xClass.isAnnotationPresent( Entity.class )
|| xClass.isAnnotationPresent( MappedSuperclass.class ) ) {
@ -319,8 +319,7 @@ public class AnnotationMetadataSourceProcessorImpl implements MetadataSourceProc
rootMetadataBuildingContext.getMetadataCollector().addAttributeConverter( descriptor );
}
@SuppressWarnings("rawtypes")
public void addAttributeConverter(Class<? extends AttributeConverter> converterClass) {
public void addAttributeConverter(Class<? extends AttributeConverter<?,?>> converterClass) {
rootMetadataBuildingContext.getMetadataCollector().addAttributeConverter( converterClass );
}
}

View File

@ -7,7 +7,6 @@
package org.hibernate.boot.spi;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -19,7 +18,6 @@ import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.annotations.CollectionTypeRegistration;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.boot.internal.ClassmateContext;
import org.hibernate.boot.internal.NamedProcedureCallDefinitionImpl;
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
import org.hibernate.boot.model.TypeDefinition;
@ -232,8 +230,7 @@ public interface InFlightMetadataCollector extends Mapping, MetadataImplementor
/**
* Apply an {@link AttributeConverter}
*/
void addAttributeConverter(Class<? extends AttributeConverter> converterClass);
void addAttributeConverter(Class<? extends AttributeConverter<?,?>> converterClass);
ConverterAutoApplyHandler getAttributeConverterAutoApplyHandler();

View File

@ -6,18 +6,18 @@
*/
package org.hibernate.cfg;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Convert;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
/**
* Describes a {@link jakarta.persistence.Convert} conversion
*
* @author Steve Ebersole
*/
public class AttributeConversionInfo {
private final Class<? extends AttributeConverter> converterClass;
private final Class<? extends AttributeConverter<?,?>> converterClass;
private final boolean conversionDisabled;
private final String attributeName;
@ -25,7 +25,7 @@ public class AttributeConversionInfo {
private final XAnnotatedElement source;
public AttributeConversionInfo(
Class<? extends AttributeConverter> converterClass,
Class<? extends AttributeConverter<?,?>> converterClass,
boolean conversionDisabled,
String attributeName,
XAnnotatedElement source) {
@ -45,7 +45,7 @@ public class AttributeConversionInfo {
);
}
public Class<? extends AttributeConverter> getConverterClass() {
public Class<? extends AttributeConverter<?,?>> getConverterClass() {
return converterClass;
}

View File

@ -65,7 +65,6 @@ public class XMLContext implements Serializable {
* @param entityMappings The xml document to add
* @return Add an xml document to this context and return the list of added class names.
*/
@SuppressWarnings("unchecked")
public List<String> addDocument(JaxbEntityMappings entityMappings) {
hasContext = true;
@ -172,7 +171,7 @@ public class XMLContext implements Serializable {
final boolean autoApply = Boolean.TRUE.equals( converterElement.isAutoApply() );
try {
final Class<? extends AttributeConverter> attributeConverterClass = classLoaderAccess.classForName(
final Class<? extends AttributeConverter<?,?>> attributeConverterClass = classLoaderAccess.classForName(
buildSafeClassName( className, packageName )
);
converterDescriptors.add(
@ -231,7 +230,7 @@ public class XMLContext implements Serializable {
return hasContext;
}
private List<ConverterDescriptor> converterDescriptors = new ArrayList<>();
private final List<ConverterDescriptor> converterDescriptors = new ArrayList<>();
public void applyDiscoveredAttributeConverters(AttributeConverterDefinitionCollector collector) {
for ( ConverterDescriptor descriptor : converterDescriptors ) {

View File

@ -1254,10 +1254,10 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil
List<ConverterDescriptor> converterDescriptors = null;
// add any explicit Class references passed in
final List<Class<? extends AttributeConverter>> loadedAnnotatedClasses = (List<Class<? extends AttributeConverter>>)
final List<Class<? extends AttributeConverter<?,?>>> loadedAnnotatedClasses = (List<Class<? extends AttributeConverter<?,?>>>)
configurationValues.remove( AvailableSettings.LOADED_CLASSES );
if ( loadedAnnotatedClasses != null ) {
for ( Class<? extends AttributeConverter> cls : loadedAnnotatedClasses ) {
for ( Class<? extends AttributeConverter<?,?>> cls : loadedAnnotatedClasses ) {
if ( AttributeConverter.class.isAssignableFrom( cls ) ) {
if ( converterDescriptors == null ) {
converterDescriptors = new ArrayList<>();

View File

@ -735,7 +735,7 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
.getService( ClassLoaderService.class );
try {
//noinspection rawtypes
final Class<AttributeConverter> converterClass = cls.classForName( converterClassName );
final Class<AttributeConverter<?,?>> converterClass = cls.classForName( converterClassName );
setAttributeConverterDescriptor( new ClassBasedConverterDescriptor(
converterClass,
false,