HHH-10050 - AttributeConverter should supports ParameterizedType if autoApply is true

This commit is contained in:
Steve Ebersole 2015-10-06 16:07:33 -05:00
parent 9633c0a6e5
commit 0cf66b85e0
22 changed files with 1002 additions and 171 deletions

View File

@ -395,7 +395,6 @@ public interface MetadataBuilder {
MetadataBuilder applyAuxiliaryDatabaseObject(AuxiliaryDatabaseObject auxiliaryDatabaseObject);
/**
* Adds an AttributeConverter by a AttributeConverterDefinition
*

View File

@ -0,0 +1,248 @@
/*
* 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.internal;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.persistence.AttributeConverter;
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.annotations.common.reflection.java.JavaXMember;
import org.hibernate.boot.spi.AttributeConverterDescriptor;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.AttributeConverterDefinition;
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;
/**
* The standard AttributeConverterDescriptor implementation
*
* @author Steve Ebersole
*/
public class AttributeConverterDescriptorImpl implements AttributeConverterDescriptor {
private final AttributeConverter attributeConverter;
private final boolean autoApply;
private final ResolvedType domainType;
private final ResolvedType jdbcType;
public static AttributeConverterDescriptor create(
AttributeConverterDefinition definition,
ClassmateContext classmateContext) {
final AttributeConverter converter = definition.getAttributeConverter();
final Class converterClass = converter.getClass();
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()
);
}
return new AttributeConverterDescriptorImpl(
converter,
definition.isAutoApply(),
converterParamTypes.get( 0 ),
converterParamTypes.get( 1 )
);
}
private AttributeConverterDescriptorImpl(
AttributeConverter attributeConverter,
boolean autoApply,
ResolvedType domainType,
ResolvedType jdbcType) {
this.attributeConverter = attributeConverter;
this.autoApply = autoApply;
this.domainType = domainType;
this.jdbcType = jdbcType;
}
@Override
public AttributeConverter getAttributeConverter() {
return attributeConverter;
}
@Override
public Class<?> getDomainType() {
return domainType.getErasedType();
}
@Override
public Class<?> getJdbcType() {
return jdbcType.getErasedType();
}
@Override
@SuppressWarnings("SimplifiableIfStatement")
public boolean shouldAutoApplyToAttribute(XProperty xProperty, MetadataBuildingContext context) {
if ( !autoApply ) {
return false;
}
final ResolvedType attributeType = resolveAttributeType( xProperty, context );
return typesMatch( domainType, attributeType );
}
private ResolvedType resolveAttributeType(XProperty xProperty, MetadataBuildingContext context) {
return resolveMember( xProperty, context ).getType();
}
private ResolvedMember resolveMember(XProperty xProperty, MetadataBuildingContext buildingContext) {
final ClassmateContext classmateContext = buildingContext.getMetadataCollector().getClassmateContext();
final ReflectionManager reflectionManager = buildingContext.getBuildingOptions().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 Method memberMethod;
private static Member toMember(XProperty xProperty) {
if ( memberMethod == null ) {
Class<JavaXMember> javaXMemberClass = JavaXMember.class;
try {
memberMethod = javaXMemberClass.getDeclaredMethod( "getMember" );
memberMethod.setAccessible( true );
}
catch (NoSuchMethodException e) {
throw new HibernateException( "Could not access org.hibernate.annotations.common.reflection.java.JavaXMember#getMember method", e );
}
}
try {
return (Member) memberMethod.invoke( xProperty );
}
catch (Exception e) {
throw new HibernateException( "Could not access org.hibernate.annotations.common.reflection.java.JavaXMember#getMember method", e );
}
}
private boolean typesMatch(ResolvedType converterDefinedType, ResolvedType checkType) {
if ( !checkType.getErasedType().isAssignableFrom( converterDefinedType.getErasedType() ) ) {
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;
}
@Override
public boolean shouldAutoApplyToCollectionElement(XProperty xProperty, MetadataBuildingContext context) {
if ( !autoApply ) {
return false;
}
final ResolvedMember collectionMember = resolveMember( xProperty, context );
final ResolvedType elementType;
if ( Map.class.isAssignableFrom( collectionMember.getType().getErasedType() ) ) {
elementType = collectionMember.getType().typeParametersFor( Map.class ).get( 1 );
}
else if ( Collection.class.isAssignableFrom( collectionMember.getType().getErasedType() ) ) {
elementType = collectionMember.getType().typeParametersFor( Collection.class ).get( 0 );
}
else {
throw new HibernateException( "Attribute was neither a Collection nor a Map : " + collectionMember.getType().getErasedType() );
}
return typesMatch( domainType, elementType );
}
@Override
public boolean shouldAutoApplyToMapKey(XProperty xProperty, MetadataBuildingContext context) {
if ( !autoApply ) {
return false;
}
final ResolvedMember collectionMember = resolveMember( xProperty, context );
final ResolvedType keyType;
if ( Map.class.isAssignableFrom( collectionMember.getType().getErasedType() ) ) {
keyType = collectionMember.getType().typeParametersFor( Map.class ).get( 0 );
}
else {
throw new HibernateException( "Attribute was not a Map : " + collectionMember.getType().getErasedType() );
}
return typesMatch( domainType, keyType );
}
}

View File

@ -0,0 +1,199 @@
/*
* 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.internal;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.persistence.AttributeConverter;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.AttributeConverterDescriptor;
import org.hibernate.boot.spi.MetadataBuildingContext;
/**
* Special-use AttributeConverterDescriptor implementation for cases where the converter will never
* be used for auto-apply.
*
* @author Steve Ebersole
*/
public class AttributeConverterDescriptorNonAutoApplicableImpl implements AttributeConverterDescriptor {
private final AttributeConverter converter;
private Class domainType;
private Class jdbcType;
public AttributeConverterDescriptorNonAutoApplicableImpl(AttributeConverter converter) {
this.converter = converter;
final Class attributeConverterClass = converter.getClass();
final ParameterizedType attributeConverterSignature = extractAttributeConverterParameterizedType(
attributeConverterClass
);
if ( attributeConverterSignature == null ) {
throw new AssertionFailure(
"Could not extract ParameterizedType representation of AttributeConverter definition " +
"from AttributeConverter implementation class [" + attributeConverterClass.getName() + "]"
);
}
if ( attributeConverterSignature.getActualTypeArguments().length < 2 ) {
throw new AnnotationException(
"AttributeConverter [" + attributeConverterClass.getName()
+ "] did not retain parameterized type information"
);
}
if ( attributeConverterSignature.getActualTypeArguments().length > 2 ) {
throw new AnnotationException(
"AttributeConverter [" + attributeConverterClass.getName()
+ "] specified more than 2 parameterized types"
);
}
this.domainType = extractClass( attributeConverterSignature.getActualTypeArguments()[0] );
if ( this.domainType == null ) {
throw new AnnotationException(
"Could not determine domain type from given AttributeConverter [" +
attributeConverterClass.getName() + "]"
);
}
this.jdbcType = extractClass(attributeConverterSignature.getActualTypeArguments()[1]);
if ( this.jdbcType == null ) {
throw new AnnotationException(
"Could not determine JDBC type from given AttributeConverter [" +
attributeConverterClass.getName() + "]"
);
}
}
private ParameterizedType extractAttributeConverterParameterizedType(Type base) {
if ( base != null ) {
Class clazz = extractClass( base );
List<Type> types = new ArrayList<Type>();
types.add( clazz.getGenericSuperclass() );
types.addAll( Arrays.asList( clazz.getGenericInterfaces() ) );
for ( Type type : types ) {
type = resolveType( type, base );
if ( ParameterizedType.class.isInstance( type ) ) {
final ParameterizedType parameterizedType = (ParameterizedType) type;
if ( AttributeConverter.class.equals( parameterizedType.getRawType() ) ) {
return parameterizedType;
}
}
ParameterizedType parameterizedType = extractAttributeConverterParameterizedType( type );
if ( parameterizedType != null ) {
return parameterizedType;
}
}
}
return null;
}
private static Class extractClass(Type type) {
if ( type instanceof Class ) {
return (Class) type;
}
else if ( type instanceof ParameterizedType ) {
return extractClass( ( (ParameterizedType) type ).getRawType() );
}
return null;
}
private static Type resolveType(Type target, Type context) {
if ( target instanceof ParameterizedType ) {
return resolveParameterizedType( (ParameterizedType) target, context );
}
else if ( target instanceof TypeVariable ) {
return resolveTypeVariable( (TypeVariable) target, (ParameterizedType) context );
}
return target;
}
private static ParameterizedType resolveParameterizedType(final ParameterizedType parameterizedType, Type context) {
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
final Type[] resolvedTypeArguments = new Type[actualTypeArguments.length];
for ( int idx = 0; idx < actualTypeArguments.length; idx++ ) {
resolvedTypeArguments[idx] = resolveType( actualTypeArguments[idx], context );
}
return new ParameterizedType() {
@Override
public Type[] getActualTypeArguments() {
return resolvedTypeArguments;
}
@Override
public Type getRawType() {
return parameterizedType.getRawType();
}
@Override
public Type getOwnerType() {
return parameterizedType.getOwnerType();
}
};
}
private static Type resolveTypeVariable(TypeVariable typeVariable, ParameterizedType context) {
Class clazz = extractClass( context.getRawType() );
TypeVariable[] typeParameters = clazz.getTypeParameters();
for ( int idx = 0; idx < typeParameters.length; idx++ ) {
if ( typeVariable.getName().equals( typeParameters[idx].getName() ) ) {
return resolveType( context.getActualTypeArguments()[idx], context );
}
}
return typeVariable;
}
private static Class extractType(TypeVariable typeVariable) {
java.lang.reflect.Type[] boundTypes = typeVariable.getBounds();
if ( boundTypes == null || boundTypes.length != 1 ) {
return null;
}
return (Class) boundTypes[0];
}
@Override
public AttributeConverter getAttributeConverter() {
return converter;
}
@Override
public Class<?> getDomainType() {
return domainType;
}
@Override
public Class<?> getJdbcType() {
return jdbcType;
}
@Override
public boolean shouldAutoApplyToAttribute(XProperty xProperty, MetadataBuildingContext context) {
return false;
}
@Override
public boolean shouldAutoApplyToCollectionElement(XProperty xProperty, MetadataBuildingContext context) {
return false;
}
@Override
public boolean shouldAutoApplyToMapKey(XProperty xProperty, MetadataBuildingContext context) {
return false;
}
}

View File

@ -0,0 +1,190 @@
/*
* 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.internal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.hibernate.AssertionFailure;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.AttributeConverterAutoApplyHandler;
import org.hibernate.boot.spi.AttributeConverterDescriptor;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.internal.util.StringHelper;
import org.jboss.logging.Logger;
/**
* @author Steve Ebersole
*/
public class AttributeConverterManager implements AttributeConverterAutoApplyHandler {
private static final Logger log = Logger.getLogger( AttributeConverterManager.class );
private Map<Class, AttributeConverterDescriptor> attributeConverterDescriptorsByClass;
void addConverter(AttributeConverterDescriptor descriptor) {
if ( attributeConverterDescriptorsByClass == null ) {
attributeConverterDescriptorsByClass = new ConcurrentHashMap<Class, AttributeConverterDescriptor>();
}
final Object old = attributeConverterDescriptorsByClass.put(
descriptor.getAttributeConverter().getClass(),
descriptor
);
if ( old != null ) {
throw new AssertionFailure(
String.format(
Locale.ENGLISH,
"AttributeConverter class [%s] registered multiple times",
descriptor.getAttributeConverter().getClass()
)
);
}
}
private Collection<AttributeConverterDescriptor> converterDescriptors() {
if ( attributeConverterDescriptorsByClass == null ) {
return Collections.emptyList();
}
return attributeConverterDescriptorsByClass.values();
}
@Override
public AttributeConverterDescriptor findAutoApplyConverterForAttribute(
XProperty xProperty,
MetadataBuildingContext context) {
List<AttributeConverterDescriptor> matched = new ArrayList<AttributeConverterDescriptor>();
for ( AttributeConverterDescriptor descriptor : converterDescriptors() ) {
log.debugf(
"Checking auto-apply AttributeConverter [%s] (type=%s) for match against attribute : %s.%s (type=%s)",
descriptor.toString(),
descriptor.getDomainType().getSimpleName(),
xProperty.getDeclaringClass().getName(),
xProperty.getName(),
xProperty.getType().getName()
);
if ( descriptor.shouldAutoApplyToAttribute( xProperty, context ) ) {
matched.add( descriptor );
}
}
if ( matched.isEmpty() ) {
return null;
}
if ( matched.size() == 1 ) {
return matched.get( 0 );
}
// otherwise, we had multiple matches
throw new RuntimeException(
String.format(
Locale.ROOT,
"Multiple auto-apply converters matched attribute [%s.%s] : %s",
xProperty.getDeclaringClass().getName(),
xProperty.getName(),
StringHelper.join( matched, RENDERER )
)
);
}
@Override
public AttributeConverterDescriptor findAutoApplyConverterForCollectionElement(
XProperty xProperty,
MetadataBuildingContext context) {
List<AttributeConverterDescriptor> matched = new ArrayList<AttributeConverterDescriptor>();
for ( AttributeConverterDescriptor descriptor : converterDescriptors() ) {
log.debugf(
"Checking auto-apply AttributeConverter [%s] (type=%s) for match against collection attribute's element : %s.%s (type=%s)",
descriptor.toString(),
descriptor.getDomainType().getSimpleName(),
xProperty.getDeclaringClass().getName(),
xProperty.getName(),
xProperty.getElementClass().getName()
);
if ( descriptor.shouldAutoApplyToCollectionElement( xProperty, context ) ) {
matched.add( descriptor );
}
}
if ( matched.isEmpty() ) {
return null;
}
if ( matched.size() == 1 ) {
return matched.get( 0 );
}
// otherwise, we had multiple matches
throw new RuntimeException(
String.format(
Locale.ROOT,
"Multiple auto-apply converters matched attribute [%s.%s] : %s",
xProperty.getDeclaringClass().getName(),
xProperty.getName(),
StringHelper.join( matched, RENDERER )
)
);
}
@Override
public AttributeConverterDescriptor findAutoApplyConverterForMapKey(
XProperty xProperty,
MetadataBuildingContext context) {
List<AttributeConverterDescriptor> matched = new ArrayList<AttributeConverterDescriptor>();
for ( AttributeConverterDescriptor descriptor : converterDescriptors() ) {
log.debugf(
"Checking auto-apply AttributeConverter [%s] (type=%s) for match against map attribute's key : %s.%s (type=%s)",
descriptor.toString(),
descriptor.getDomainType().getSimpleName(),
xProperty.getDeclaringClass().getName(),
xProperty.getName(),
xProperty.getMapKey().getName()
);
if ( descriptor.shouldAutoApplyToMapKey( xProperty, context ) ) {
matched.add( descriptor );
}
}
if ( matched.isEmpty() ) {
return null;
}
if ( matched.size() == 1 ) {
return matched.get( 0 );
}
// otherwise, we had multiple matches
throw new RuntimeException(
String.format(
Locale.ROOT,
"Multiple auto-apply converters matched attribute [%s.%s] : %s",
xProperty.getDeclaringClass().getName(),
xProperty.getName(),
StringHelper.join( matched, RENDERER )
)
);
}
private static StringHelper.Renderer<AttributeConverterDescriptor> RENDERER = new StringHelper.Renderer<AttributeConverterDescriptor>() {
@Override
public String render(AttributeConverterDescriptor value) {
return value.getAttributeConverter().getClass().getName();
}
};
}

View File

@ -0,0 +1,37 @@
/*
* 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.internal;
import com.fasterxml.classmate.MemberResolver;
import com.fasterxml.classmate.TypeResolver;
/**
* @author Steve Ebersole
*/
public class ClassmateContext {
private TypeResolver typeResolver = new TypeResolver();
private MemberResolver memberResolver = new MemberResolver( typeResolver );
public TypeResolver getTypeResolver() {
if ( typeResolver == null ) {
throw new IllegalStateException( "Classmate context has been released" );
}
return typeResolver;
}
public MemberResolver getMemberResolver() {
if ( memberResolver == null ) {
throw new IllegalStateException( "Classmate context has been released" );
}
return memberResolver;
}
public void release() {
typeResolver = null;
memberResolver = null;
}
}

View File

@ -48,6 +48,8 @@ import org.hibernate.boot.model.relational.Namespace;
import org.hibernate.boot.model.source.internal.ConstraintSecondPass;
import org.hibernate.boot.model.source.internal.ImplicitColumnNamingSecondPass;
import org.hibernate.boot.model.source.spi.LocalMetadataBuildingContext;
import org.hibernate.boot.spi.AttributeConverterAutoApplyHandler;
import org.hibernate.boot.spi.AttributeConverterDescriptor;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.MetadataBuildingOptions;
@ -115,6 +117,9 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
private final MetadataBuildingOptions options;
private final TypeResolver typeResolver;
private final AttributeConverterManager attributeConverterManager = new AttributeConverterManager();
private final ClassmateContext classmateContext = new ClassmateContext();
private final UUID uuid;
private final MutableIdentifierGeneratorFactory identifierGeneratorFactory;
@ -136,10 +141,8 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
private final Map<String, FetchProfile> fetchProfileMap = new HashMap<String, FetchProfile>();
private final Map<String, IdentifierGeneratorDefinition> idGeneratorDefinitionMap = new HashMap<String, IdentifierGeneratorDefinition>();
private Map<Class, AttributeConverterDefinition> attributeConverterDefinitionsByClass;
private Map<String, SQLFunction> sqlFunctionMap;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// All the annotation-processing-specific state :(
private final Set<String> defaultIdentifierGeneratorNames = new HashSet<String>();
@ -335,68 +338,33 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
}
}
@Override
public ClassmateContext getClassmateContext() {
return classmateContext;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// attribute converters
@Override
public void addAttributeConverter(AttributeConverterDefinition definition) {
if ( attributeConverterDefinitionsByClass == null ) {
attributeConverterDefinitionsByClass = new ConcurrentHashMap<Class, AttributeConverterDefinition>();
}
final Object old = attributeConverterDefinitionsByClass.put(
definition.getAttributeConverter().getClass(),
definition
);
if ( old != null ) {
throw new AssertionFailure(
String.format(
Locale.ENGLISH,
"AttributeConverter class [%s] registered multiple times",
definition.getAttributeConverter().getClass()
)
);
}
}
public static AttributeConverter instantiateAttributeConverter(Class<? extends AttributeConverter> attributeConverterClass) {
try {
return attributeConverterClass.newInstance();
}
catch (Exception e) {
throw new AnnotationException(
"Unable to instantiate AttributeConverter [" + attributeConverterClass.getName() + "]",
e
);
}
}
public static AttributeConverterDefinition toAttributeConverterDefinition(AttributeConverter attributeConverter) {
boolean autoApply = false;
Converter converterAnnotation = attributeConverter.getClass().getAnnotation( Converter.class );
if ( converterAnnotation != null ) {
autoApply = converterAnnotation.autoApply();
}
return new AttributeConverterDefinition( attributeConverter, autoApply );
}
@Override
public void addAttributeConverter(Class<? extends AttributeConverter> converterClass) {
addAttributeConverter(
toAttributeConverterDefinition(
instantiateAttributeConverter( converterClass )
attributeConverterManager.addConverter(
AttributeConverterDescriptorImpl.create(
definition,
classmateContext
)
);
}
@Override
public java.util.Collection<AttributeConverterDefinition> getAttributeConverters() {
if ( attributeConverterDefinitionsByClass == null ) {
return Collections.emptyList();
}
return attributeConverterDefinitionsByClass.values();
public void addAttributeConverter(Class<? extends AttributeConverter> converterClass) {
addAttributeConverter( AttributeConverterDefinition.from( converterClass ) );
}
@Override
public AttributeConverterAutoApplyHandler getAttributeConverterAutoApplyHandler() {
return attributeConverterManager;
}
@ -2164,27 +2132,32 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
processSecondPasses( buildingContext );
processExportableProducers( buildingContext );
return new MetadataImpl(
uuid,
options,
typeResolver,
identifierGeneratorFactory,
entityBindingMap,
mappedSuperClasses,
collectionBindingMap,
typeDefinitionMap,
filterDefinitionMap,
fetchProfileMap,
imports,
idGeneratorDefinitionMap,
namedQueryMap,
namedNativeQueryMap,
namedProcedureCallMap,
sqlResultSetMappingMap,
namedEntityGraphMap,
sqlFunctionMap,
getDatabase()
);
try {
return new MetadataImpl(
uuid,
options,
typeResolver,
identifierGeneratorFactory,
entityBindingMap,
mappedSuperClasses,
collectionBindingMap,
typeDefinitionMap,
filterDefinitionMap,
fetchProfileMap,
imports,
idGeneratorDefinitionMap,
namedQueryMap,
namedNativeQueryMap,
namedProcedureCallMap,
sqlResultSetMappingMap,
namedEntityGraphMap,
sqlFunctionMap,
getDatabase()
);
}
finally {
classmateContext.release();
}
}
private void processExportableProducers(MetadataBuildingContext buildingContext) {

View File

@ -0,0 +1,18 @@
/*
* 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.spi;
import org.hibernate.annotations.common.reflection.XProperty;
/**
* @author Steve Ebersole
*/
public interface AttributeConverterAutoApplyHandler {
AttributeConverterDescriptor findAutoApplyConverterForAttribute(XProperty xProperty, MetadataBuildingContext context);
AttributeConverterDescriptor findAutoApplyConverterForCollectionElement(XProperty xProperty, MetadataBuildingContext context);
AttributeConverterDescriptor findAutoApplyConverterForMapKey(XProperty xProperty, MetadataBuildingContext context);
}

View File

@ -0,0 +1,27 @@
/*
* 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.spi;
import javax.persistence.AttributeConverter;
import org.hibernate.annotations.common.reflection.XProperty;
/**
* Internal descriptor for an AttributeConverter implementation.
*
* @author Steve Ebersole
*/
public interface AttributeConverterDescriptor {
AttributeConverter getAttributeConverter();
Class<?> getDomainType();
Class<?> getJdbcType();
boolean shouldAutoApplyToAttribute(XProperty xProperty, MetadataBuildingContext context);
boolean shouldAutoApplyToCollectionElement(XProperty xProperty, MetadataBuildingContext context);
boolean shouldAutoApplyToMapKey(XProperty xProperty, MetadataBuildingContext context);
}

View File

@ -17,6 +17,7 @@ import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.annotations.AnyMetaDef;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.boot.internal.ClassmateContext;
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
import org.hibernate.boot.model.TypeDefinition;
import org.hibernate.boot.model.naming.Identifier;
@ -213,7 +214,9 @@ public interface InFlightMetadataCollector extends Mapping, MetadataImplementor
void addAttributeConverter(AttributeConverterDefinition converter);
void addAttributeConverter(Class<? extends AttributeConverter> converterClass);
java.util.Collection<AttributeConverterDefinition> getAttributeConverters();
AttributeConverterAutoApplyHandler getAttributeConverterAutoApplyHandler();
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// second passes
@ -275,6 +278,8 @@ public interface InFlightMetadataCollector extends Mapping, MetadataImplementor
NaturalIdUniqueKeyBinder locateNaturalIdUniqueKeyBinder(String entityName);
void registerNaturalIdUniqueKeyBinder(String entityName, NaturalIdUniqueKeyBinder ukBinder);
ClassmateContext getClassmateContext();
interface DelayedPropertyReferenceHandler extends Serializable {
void process(InFlightMetadataCollector metadataCollector);
}

View File

@ -24,6 +24,8 @@ import org.hibernate.AssertionFailure;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.internal.AttributeConverterDescriptorImpl;
import org.hibernate.boot.spi.AttributeConverterDescriptor;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.util.StringHelper;
@ -68,7 +70,7 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
protected abstract AttributeConversionInfo locateAttributeConversionInfo(String path);
@Override
public AttributeConverterDefinition resolveAttributeConverterDefinition(XProperty property) {
public AttributeConverterDescriptor resolveAttributeConverterDescriptor(XProperty property) {
AttributeConversionInfo info = locateAttributeConversionInfo( property );
if ( info != null ) {
if ( info.isConversionDisabled() ) {
@ -76,7 +78,7 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
}
else {
try {
return makeAttributeConverterDefinition( info );
return makeAttributeConverterDescriptor( info );
}
catch (Exception e) {
throw new IllegalStateException(
@ -89,28 +91,15 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
log.debugf( "Attempting to locate auto-apply AttributeConverter for property [%s:%s]", path, property.getName() );
final Class propertyType = context.getBuildingOptions().getReflectionManager().toClass( property.getType() );
for ( AttributeConverterDefinition attributeConverterDefinition : context.getMetadataCollector().getAttributeConverters() ) {
if ( ! attributeConverterDefinition.isAutoApply() ) {
continue;
}
log.debugf(
"Checking auto-apply AttributeConverter [%s] type [%s] for match [%s]",
attributeConverterDefinition.toString(),
attributeConverterDefinition.getEntityAttributeType().getSimpleName(),
propertyType.getSimpleName()
);
if ( areTypeMatch( attributeConverterDefinition.getEntityAttributeType(), propertyType ) ) {
return attributeConverterDefinition;
}
}
return null;
return context.getMetadataCollector()
.getAttributeConverterAutoApplyHandler()
.findAutoApplyConverterForAttribute( property, context );
}
protected AttributeConverterDefinition makeAttributeConverterDefinition(AttributeConversionInfo conversion) {
protected AttributeConverterDescriptor makeAttributeConverterDescriptor(AttributeConversionInfo conversion) {
try {
return new AttributeConverterDefinition( conversion.getConverterClass().newInstance(), false );
AttributeConverterDefinition definition = new AttributeConverterDefinition( conversion.getConverterClass().newInstance(), false );
return AttributeConverterDescriptorImpl.create( definition, context.getMetadataCollector().getClassmateContext() );
}
catch (Exception e) {
throw new AnnotationException( "Unable to create AttributeConverter instance", e );

View File

@ -12,20 +12,20 @@ import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.jboss.logging.Logger;
/**
* Externalized representation of an AttributeConverter
*
* @author Steve Ebersole
*
* @see org.hibernate.boot.spi.AttributeConverterDescriptor
*/
public class AttributeConverterDefinition {
private static final Logger log = Logger.getLogger( AttributeConverterDefinition.class );
private final AttributeConverter attributeConverter;
private final boolean autoApply;
private final Class entityAttributeType;

View File

@ -25,7 +25,7 @@ import org.hibernate.annotations.ManyToAny;
import org.hibernate.annotations.MapKeyType;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.model.source.spi.AttributePath;
import org.hibernate.boot.spi.AttributeConverterDescriptor;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
@ -345,7 +345,7 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
}
}
public AttributeConverterDefinition resolveElementAttributeConverterDefinition(XClass elementXClass) {
public AttributeConverterDescriptor resolveElementAttributeConverterDescriptor(XProperty collectionXProperty, XClass elementXClass) {
AttributeConversionInfo info = locateAttributeConversionInfo( "element" );
if ( info != null ) {
if ( info.isConversionDisabled() ) {
@ -353,7 +353,7 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
}
else {
try {
return makeAttributeConverterDefinition( info );
return makeAttributeConverterDescriptor( info );
}
catch (Exception e) {
throw new IllegalStateException(
@ -369,25 +369,11 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
collection.getRole()
);
final Class elementClass = determineElementClass( elementXClass );
if ( elementClass != null ) {
for ( AttributeConverterDefinition attributeConverterDefinition : getContext().getMetadataCollector().getAttributeConverters() ) {
if ( ! attributeConverterDefinition.isAutoApply() ) {
continue;
}
log.debugf(
"Checking auto-apply AttributeConverter [%s] type [%s] for match [%s]",
attributeConverterDefinition.toString(),
attributeConverterDefinition.getEntityAttributeType().getSimpleName(),
elementClass.getSimpleName()
);
if ( areTypeMatch( attributeConverterDefinition.getEntityAttributeType(), elementClass ) ) {
return attributeConverterDefinition;
}
}
}
// todo : do we need to pass along `XClass elementXClass`?
return null;
return getContext().getMetadataCollector()
.getAttributeConverterAutoApplyHandler()
.findAutoApplyConverterForCollectionElement( collectionXProperty, getContext() );
}
private Class determineElementClass(XClass elementXClass) {
@ -419,7 +405,7 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
return null;
}
public AttributeConverterDefinition keyElementAttributeConverterDefinition(XClass keyXClass) {
public AttributeConverterDescriptor mapKeyAttributeConverterDescriptor(XProperty mapXProperty, XClass keyXClass) {
AttributeConversionInfo info = locateAttributeConversionInfo( "key" );
if ( info != null ) {
if ( info.isConversionDisabled() ) {
@ -427,7 +413,7 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
}
else {
try {
return makeAttributeConverterDefinition( info );
return makeAttributeConverterDescriptor( info );
}
catch (Exception e) {
throw new IllegalStateException(
@ -443,25 +429,11 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
collection.getRole()
);
final Class elementClass = determineKeyClass( keyXClass );
if ( elementClass != null ) {
for ( AttributeConverterDefinition attributeConverterDefinition : getContext().getMetadataCollector().getAttributeConverters() ) {
if ( ! attributeConverterDefinition.isAutoApply() ) {
continue;
}
log.debugf(
"Checking auto-apply AttributeConverter [%s] type [%s] for match [%s]",
attributeConverterDefinition.toString(),
attributeConverterDefinition.getEntityAttributeType().getSimpleName(),
elementClass.getSimpleName()
);
if ( areTypeMatch( attributeConverterDefinition.getEntityAttributeType(), elementClass ) ) {
return attributeConverterDefinition;
}
}
}
// todo : do we need to pass along `XClass keyXClass`?
return null;
return getContext().getMetadataCollector()
.getAttributeConverterAutoApplyHandler()
.findAutoApplyConverterForMapKey( mapXProperty, getContext() );
}
private Class determineKeyClass(XClass keyXClass) {

View File

@ -12,6 +12,7 @@ import javax.persistence.JoinTable;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.AttributeConverterDescriptor;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.PersistentClass;
@ -91,5 +92,5 @@ public interface PropertyHolder {
* @param property
* @return
*/
AttributeConverterDefinition resolveAttributeConverterDefinition(XProperty property);
AttributeConverterDescriptor resolveAttributeConverterDescriptor(XProperty property);
}

View File

@ -1460,7 +1460,7 @@ public abstract class CollectionBinder {
property,
elementClass,
collValue.getOwnerEntityName(),
holder.resolveElementAttributeConverterDefinition( elementClass )
holder.resolveElementAttributeConverterDescriptor( property, elementClass )
);
elementBinder.setPersistentClassName( propertyHolder.getEntityName() );
elementBinder.setAccessType( accessType );

View File

@ -275,7 +275,7 @@ public class MapBinder extends CollectionBinder {
property,
keyXClass,
this.collection.getOwnerEntityName(),
holder.keyElementAttributeConverterDefinition( keyXClass )
holder.mapKeyAttributeConverterDescriptor( property, keyXClass )
);
elementBinder.setPersistentClassName( propertyHolder.getEntityName() );
elementBinder.setAccessType( accessType );

View File

@ -180,7 +180,7 @@ public class PropertyBinder {
property,
returnedClass,
containerClassName,
holder.resolveAttributeConverterDefinition( property )
holder.resolveAttributeConverterDescriptor( property )
);
simpleValueBinder.setReferencedEntityName( referencedEntityName );
simpleValueBinder.setAccessType( accessType );

View File

@ -31,6 +31,7 @@ import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.annotations.common.util.StandardClassLoaderDelegateImpl;
import org.hibernate.boot.model.TypeDefinition;
import org.hibernate.boot.spi.AttributeConverterDescriptor;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.AccessType;
import org.hibernate.cfg.AttributeConverterDefinition;
@ -86,7 +87,7 @@ public class SimpleValueBinder {
private XProperty xproperty;
private AccessType accessType;
private AttributeConverterDefinition attributeConverterDefinition;
private AttributeConverterDescriptor attributeConverterDescriptor;
public void setReferencedEntityName(String referencedEntityName) {
this.referencedEntityName = referencedEntityName;
@ -131,7 +132,7 @@ public class SimpleValueBinder {
//TODO execute it lazily to be order safe
public void setType(XProperty property, XClass returnedClass, String declaringClassName, AttributeConverterDefinition attributeConverterDefinition) {
public void setType(XProperty property, XClass returnedClass, String declaringClassName, AttributeConverterDescriptor attributeConverterDescriptor) {
if ( returnedClass == null ) {
// we cannot guess anything
return;
@ -302,11 +303,11 @@ public class SimpleValueBinder {
defaultType = BinderHelper.isEmptyAnnotationValue( type ) ? returnedClassName : type;
this.typeParameters = typeParameters;
applyAttributeConverter( property, attributeConverterDefinition );
applyAttributeConverter( property, attributeConverterDescriptor );
}
private void applyAttributeConverter(XProperty property, AttributeConverterDefinition attributeConverterDefinition) {
if ( attributeConverterDefinition == null ) {
private void applyAttributeConverter(XProperty property, AttributeConverterDescriptor attributeConverterDescriptor) {
if ( attributeConverterDescriptor == null ) {
return;
}
@ -337,7 +338,7 @@ public class SimpleValueBinder {
return;
}
this.attributeConverterDefinition = attributeConverterDefinition;
this.attributeConverterDescriptor = attributeConverterDescriptor;
}
private boolean isAssociation() {
@ -429,7 +430,7 @@ public class SimpleValueBinder {
public void fillSimpleValue() {
LOG.debugf( "Starting fillSimpleValue for %s", propertyName );
if ( attributeConverterDefinition != null ) {
if ( attributeConverterDescriptor != null ) {
if ( ! BinderHelper.isEmptyAnnotationValue( explicitType ) ) {
throw new AnnotationException(
String.format(
@ -442,11 +443,11 @@ public class SimpleValueBinder {
}
LOG.debugf(
"Applying JPA AttributeConverter [%s] to [%s:%s]",
attributeConverterDefinition,
attributeConverterDescriptor,
persistentClassName,
propertyName
);
simpleValue.setJpaAttributeConverterDefinition( attributeConverterDefinition );
simpleValue.setJpaAttributeConverterDescriptor( attributeConverterDescriptor );
}
else {
String type;
@ -480,7 +481,7 @@ public class SimpleValueBinder {
simpleValue.setTypeName( type );
}
if ( persistentClassName != null || attributeConverterDefinition != null ) {
if ( persistentClassName != null || attributeConverterDescriptor != null ) {
try {
simpleValue.setTypeUsingReflection( persistentClassName, propertyName );
}

View File

@ -13,6 +13,7 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@ -803,4 +804,29 @@ public final class StringHelper {
public static List<String> parseCommaSeparatedString(String incomingString) {
return Arrays.asList( incomingString.split( "\\s*,\\s*" ) );
}
public static <T> String join(Collection<T> values, Renderer<T> renderer) {
final StringBuilder buffer = new StringBuilder();
boolean firstPass = true;
for ( T value : values ) {
if ( firstPass ) {
firstPass = false;
}
else {
buffer.append( ", " );
}
buffer.append( renderer.render( value ) );
}
return buffer.toString();
}
public static <T> String join(T[] values, Renderer<T> renderer) {
return join( Arrays.asList( values ), renderer );
}
public interface Renderer<T> {
String render(T value);
}
}

View File

@ -19,10 +19,11 @@ import javax.persistence.AttributeConverter;
import org.hibernate.FetchMode;
import org.hibernate.MappingException;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.internal.AttributeConverterDescriptorNonAutoApplicableImpl;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.boot.spi.AttributeConverterDescriptor;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.cfg.AttributeConverterDefinition;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.config.spi.ConfigurationService;
@ -75,7 +76,7 @@ public class SimpleValue implements KeyValue {
private boolean alternateUniqueKey;
private boolean cascadeDeleteEnabled;
private AttributeConverterDefinition attributeConverterDefinition;
private AttributeConverterDescriptor attributeConverterDescriptor;
private Type type;
public SimpleValue(MetadataImplementor metadata) {
@ -155,7 +156,7 @@ public class SimpleValue implements KeyValue {
.getService( ClassLoaderService.class );
try {
final Class<AttributeConverter> converterClass = cls.classForName( converterClassName );
attributeConverterDefinition = new AttributeConverterDefinition( converterClass.newInstance(), false );
attributeConverterDescriptor = new AttributeConverterDescriptorNonAutoApplicableImpl( converterClass.newInstance() );
return;
}
catch (Exception e) {
@ -420,7 +421,7 @@ public class SimpleValue implements KeyValue {
return;
}
if ( attributeConverterDefinition == null ) {
if ( attributeConverterDescriptor == null ) {
// this is here to work like legacy. This should change when we integrate with metamodel to
// look for SqlTypeDescriptor and JavaTypeDescriptor individually and create the BasicType (well, really
// keep a registry of [SqlTypeDescriptor,JavaTypeDescriptor] -> BasicType...)
@ -475,8 +476,8 @@ public class SimpleValue implements KeyValue {
private Type buildAttributeConverterTypeAdapter() {
// todo : validate the number of columns present here?
final Class entityAttributeJavaType = attributeConverterDefinition.getEntityAttributeType();
final Class databaseColumnJavaType = attributeConverterDefinition.getDatabaseColumnType();
final Class entityAttributeJavaType = attributeConverterDescriptor.getDomainType();
final Class databaseColumnJavaType = attributeConverterDescriptor.getJdbcType();
// resolve the JavaTypeDescriptor ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -523,14 +524,14 @@ public class SimpleValue implements KeyValue {
// and finally construct the adapter, which injects the AttributeConverter calls into the binding/extraction
// process...
final SqlTypeDescriptor sqlTypeDescriptorAdapter = new AttributeConverterSqlTypeDescriptorAdapter(
attributeConverterDefinition.getAttributeConverter(),
attributeConverterDescriptor.getAttributeConverter(),
sqlTypeDescriptor,
intermediateJavaTypeDescriptor
);
// todo : cache the AttributeConverterTypeAdapter in case that AttributeConverter is applied multiple times.
final String name = AttributeConverterTypeAdapter.NAME_PREFIX + attributeConverterDefinition.getAttributeConverter().getClass().getName();
final String name = AttributeConverterTypeAdapter.NAME_PREFIX + attributeConverterDescriptor.getAttributeConverter().getClass().getName();
final String description = String.format(
"BasicType adapter for AttributeConverter<%s,%s>",
entityAttributeJavaType.getSimpleName(),
@ -539,7 +540,7 @@ public class SimpleValue implements KeyValue {
return new AttributeConverterTypeAdapter(
name,
description,
attributeConverterDefinition.getAttributeConverter(),
attributeConverterDescriptor.getAttributeConverter(),
sqlTypeDescriptorAdapter,
entityAttributeJavaType,
databaseColumnJavaType,
@ -583,8 +584,8 @@ public class SimpleValue implements KeyValue {
return getColumnInsertability();
}
public void setJpaAttributeConverterDefinition(AttributeConverterDefinition attributeConverterDefinition) {
this.attributeConverterDefinition = attributeConverterDefinition;
public void setJpaAttributeConverterDescriptor(AttributeConverterDescriptor attributeConverterDescriptor) {
this.attributeConverterDescriptor = attributeConverterDescriptor;
}
private void createParameterImpl() {

View File

@ -22,6 +22,7 @@ import org.hibernate.IrrelevantEntity;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.internal.AttributeConverterDescriptorNonAutoApplicableImpl;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.spi.MetadataImplementor;
@ -96,8 +97,8 @@ public class AttributeConverterTest extends BaseUnitTestCase {
try {
MetadataImplementor metadata = (MetadataImplementor) new MetadataSources( ssr ).buildMetadata();
SimpleValue simpleValue = new SimpleValue( metadata );
simpleValue.setJpaAttributeConverterDefinition(
new AttributeConverterDefinition( new StringClobConverter(), true )
simpleValue.setJpaAttributeConverterDescriptor(
new AttributeConverterDescriptorNonAutoApplicableImpl( new StringClobConverter() )
);
simpleValue.setTypeUsingReflection( IrrelevantEntity.class.getName(), "name" );

View File

@ -8,14 +8,29 @@ package org.hibernate.test.converter.generics;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AttributeConverterDefinition;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.type.descriptor.converter.AttributeConverterTypeAdapter;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertEquals;
/**
@ -27,6 +42,20 @@ import static org.junit.Assert.assertEquals;
*/
public class ParameterizedAttributeConverterParameterTypeTest extends BaseUnitTestCase {
private static StandardServiceRegistry ssr;
@BeforeClass
public static void beforeClass() {
ssr = new StandardServiceRegistryBuilder().build();
}
@AfterClass
public static void afterClass() {
if ( ssr != null ) {
StandardServiceRegistryBuilder.destroy( ssr );
}
}
public static class CustomAttributeConverter implements AttributeConverter<List<String>, Integer> {
@Override
public Integer convertToDatabaseColumn(List<String> attribute) {
@ -46,4 +75,119 @@ public class ParameterizedAttributeConverterParameterTypeTest extends BaseUnitTe
assertEquals( List.class, def.getEntityAttributeType() );
}
@Test
@TestForIssue( jiraKey = "HHH-10050" )
public void testNestedTypeParameterAutoApplication() {
final Metadata metadata = new MetadataSources( ssr )
.addAnnotatedClass( SampleEntity.class )
.getMetadataBuilder()
.applyAttributeConverter( IntegerListConverter.class )
.applyAttributeConverter( StringListConverter.class )
.build();
// lets make sure the auto-apply converters were applied properly...
PersistentClass pc = metadata.getEntityBinding( SampleEntity.class.getName() );
{
Property prop = pc.getProperty( "someStrings" );
AttributeConverterTypeAdapter type = assertTyping(
AttributeConverterTypeAdapter.class,
prop.getType()
);
assertTyping(
StringListConverter.class,
type.getAttributeConverter()
);
}
{
Property prop = pc.getProperty( "someIntegers" );
AttributeConverterTypeAdapter type = assertTyping(
AttributeConverterTypeAdapter.class,
prop.getType()
);
assertTyping(
IntegerListConverter.class,
type.getAttributeConverter()
);
}
}
@Entity
public static class SampleEntity {
@Id
private Integer id;
private List<String> someStrings;
private List<Integer> someIntegers;
}
@Converter( autoApply = true )
public static class IntegerListConverter implements AttributeConverter<List<Integer>,String> {
@Override
public String convertToDatabaseColumn(List<Integer> attribute) {
if ( attribute == null || attribute.isEmpty() ) {
return null;
}
else {
return StringHelper.join( ", ", attribute );
}
}
@Override
public List<Integer> convertToEntityAttribute(String dbData) {
if ( dbData == null ) {
return null;
}
dbData = dbData.trim();
if ( dbData.length() == 0 ) {
return null;
}
final List<Integer> integers = new ArrayList<Integer>();
final StringTokenizer tokens = new StringTokenizer( dbData, "," );
while ( tokens.hasMoreTokens() ) {
integers.add( Integer.valueOf( tokens.nextToken() ) );
}
return integers;
}
}
@Converter( autoApply = true )
public static class StringListConverter implements AttributeConverter<List<String>,String> {
@Override
public String convertToDatabaseColumn(List<String> attribute) {
if ( attribute == null || attribute.isEmpty() ) {
return null;
}
else {
return StringHelper.join( ", ", attribute );
}
}
@Override
public List<String> convertToEntityAttribute(String dbData) {
if ( dbData == null ) {
return null;
}
dbData = dbData.trim();
if ( dbData.length() == 0 ) {
return null;
}
final List<String> strings = new ArrayList<String>();
final StringTokenizer tokens = new StringTokenizer( dbData, "," );
while ( tokens.hasMoreTokens() ) {
strings.add( tokens.nextToken() );
}
return strings;
}
}
}

View File

@ -27,7 +27,7 @@ ext {
// Annotations
commons_annotations: 'org.hibernate.common:hibernate-commons-annotations:5.0.0.Final',
jandex: 'org.jboss:jandex:2.0.0.CR1',
classmate: 'com.fasterxml:classmate:0.8.0',
classmate: 'com.fasterxml:classmate:1.3.0',
// Woodstox
woodstox: "org.codehaus.woodstox:woodstox-core-asl:4.3.0",