HHH-10050 - AttributeConverter should supports ParameterizedType if autoApply is true
This commit is contained in:
parent
9633c0a6e5
commit
0cf66b85e0
|
@ -395,7 +395,6 @@ public interface MetadataBuilder {
|
||||||
|
|
||||||
MetadataBuilder applyAuxiliaryDatabaseObject(AuxiliaryDatabaseObject auxiliaryDatabaseObject);
|
MetadataBuilder applyAuxiliaryDatabaseObject(AuxiliaryDatabaseObject auxiliaryDatabaseObject);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an AttributeConverter by a AttributeConverterDefinition
|
* Adds an AttributeConverter by a AttributeConverterDefinition
|
||||||
*
|
*
|
||||||
|
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.ConstraintSecondPass;
|
||||||
import org.hibernate.boot.model.source.internal.ImplicitColumnNamingSecondPass;
|
import org.hibernate.boot.model.source.internal.ImplicitColumnNamingSecondPass;
|
||||||
import org.hibernate.boot.model.source.spi.LocalMetadataBuildingContext;
|
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.InFlightMetadataCollector;
|
||||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||||
import org.hibernate.boot.spi.MetadataBuildingOptions;
|
import org.hibernate.boot.spi.MetadataBuildingOptions;
|
||||||
|
@ -115,6 +117,9 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
|
||||||
private final MetadataBuildingOptions options;
|
private final MetadataBuildingOptions options;
|
||||||
private final TypeResolver typeResolver;
|
private final TypeResolver typeResolver;
|
||||||
|
|
||||||
|
private final AttributeConverterManager attributeConverterManager = new AttributeConverterManager();
|
||||||
|
private final ClassmateContext classmateContext = new ClassmateContext();
|
||||||
|
|
||||||
private final UUID uuid;
|
private final UUID uuid;
|
||||||
private final MutableIdentifierGeneratorFactory identifierGeneratorFactory;
|
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, FetchProfile> fetchProfileMap = new HashMap<String, FetchProfile>();
|
||||||
private final Map<String, IdentifierGeneratorDefinition> idGeneratorDefinitionMap = new HashMap<String, IdentifierGeneratorDefinition>();
|
private final Map<String, IdentifierGeneratorDefinition> idGeneratorDefinitionMap = new HashMap<String, IdentifierGeneratorDefinition>();
|
||||||
|
|
||||||
private Map<Class, AttributeConverterDefinition> attributeConverterDefinitionsByClass;
|
|
||||||
private Map<String, SQLFunction> sqlFunctionMap;
|
private Map<String, SQLFunction> sqlFunctionMap;
|
||||||
|
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// All the annotation-processing-specific state :(
|
// All the annotation-processing-specific state :(
|
||||||
private final Set<String> defaultIdentifierGeneratorNames = new HashSet<String>();
|
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
|
// attribute converters
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addAttributeConverter(AttributeConverterDefinition definition) {
|
public void addAttributeConverter(AttributeConverterDefinition definition) {
|
||||||
if ( attributeConverterDefinitionsByClass == null ) {
|
attributeConverterManager.addConverter(
|
||||||
attributeConverterDefinitionsByClass = new ConcurrentHashMap<Class, AttributeConverterDefinition>();
|
AttributeConverterDescriptorImpl.create(
|
||||||
}
|
definition,
|
||||||
|
classmateContext
|
||||||
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
|
@Override
|
||||||
public void addAttributeConverter(Class<? extends AttributeConverter> converterClass) {
|
public void addAttributeConverter(Class<? extends AttributeConverter> converterClass) {
|
||||||
addAttributeConverter(
|
addAttributeConverter( AttributeConverterDefinition.from( converterClass ) );
|
||||||
toAttributeConverterDefinition(
|
|
||||||
instantiateAttributeConverter( converterClass )
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public java.util.Collection<AttributeConverterDefinition> getAttributeConverters() {
|
public AttributeConverterAutoApplyHandler getAttributeConverterAutoApplyHandler() {
|
||||||
if ( attributeConverterDefinitionsByClass == null ) {
|
return attributeConverterManager;
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return attributeConverterDefinitionsByClass.values();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2164,6 +2132,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
|
||||||
processSecondPasses( buildingContext );
|
processSecondPasses( buildingContext );
|
||||||
processExportableProducers( buildingContext );
|
processExportableProducers( buildingContext );
|
||||||
|
|
||||||
|
try {
|
||||||
return new MetadataImpl(
|
return new MetadataImpl(
|
||||||
uuid,
|
uuid,
|
||||||
options,
|
options,
|
||||||
|
@ -2186,6 +2155,10 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
|
||||||
getDatabase()
|
getDatabase()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
classmateContext.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void processExportableProducers(MetadataBuildingContext buildingContext) {
|
private void processExportableProducers(MetadataBuildingContext buildingContext) {
|
||||||
// for now we only handle id generators as ExportableProducers
|
// for now we only handle id generators as ExportableProducers
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ import org.hibernate.HibernateException;
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
import org.hibernate.annotations.AnyMetaDef;
|
import org.hibernate.annotations.AnyMetaDef;
|
||||||
import org.hibernate.annotations.common.reflection.XClass;
|
import org.hibernate.annotations.common.reflection.XClass;
|
||||||
|
import org.hibernate.boot.internal.ClassmateContext;
|
||||||
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
|
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
|
||||||
import org.hibernate.boot.model.TypeDefinition;
|
import org.hibernate.boot.model.TypeDefinition;
|
||||||
import org.hibernate.boot.model.naming.Identifier;
|
import org.hibernate.boot.model.naming.Identifier;
|
||||||
|
@ -213,7 +214,9 @@ public interface InFlightMetadataCollector extends Mapping, MetadataImplementor
|
||||||
|
|
||||||
void addAttributeConverter(AttributeConverterDefinition converter);
|
void addAttributeConverter(AttributeConverterDefinition converter);
|
||||||
void addAttributeConverter(Class<? extends AttributeConverter> converterClass);
|
void addAttributeConverter(Class<? extends AttributeConverter> converterClass);
|
||||||
java.util.Collection<AttributeConverterDefinition> getAttributeConverters();
|
|
||||||
|
AttributeConverterAutoApplyHandler getAttributeConverterAutoApplyHandler();
|
||||||
|
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// second passes
|
// second passes
|
||||||
|
@ -275,6 +278,8 @@ public interface InFlightMetadataCollector extends Mapping, MetadataImplementor
|
||||||
NaturalIdUniqueKeyBinder locateNaturalIdUniqueKeyBinder(String entityName);
|
NaturalIdUniqueKeyBinder locateNaturalIdUniqueKeyBinder(String entityName);
|
||||||
void registerNaturalIdUniqueKeyBinder(String entityName, NaturalIdUniqueKeyBinder ukBinder);
|
void registerNaturalIdUniqueKeyBinder(String entityName, NaturalIdUniqueKeyBinder ukBinder);
|
||||||
|
|
||||||
|
ClassmateContext getClassmateContext();
|
||||||
|
|
||||||
interface DelayedPropertyReferenceHandler extends Serializable {
|
interface DelayedPropertyReferenceHandler extends Serializable {
|
||||||
void process(InFlightMetadataCollector metadataCollector);
|
void process(InFlightMetadataCollector metadataCollector);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,8 @@ import org.hibernate.AssertionFailure;
|
||||||
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
|
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
|
||||||
import org.hibernate.annotations.common.reflection.XClass;
|
import org.hibernate.annotations.common.reflection.XClass;
|
||||||
import org.hibernate.annotations.common.reflection.XProperty;
|
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.boot.spi.MetadataBuildingContext;
|
||||||
import org.hibernate.internal.CoreLogging;
|
import org.hibernate.internal.CoreLogging;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
|
@ -68,7 +70,7 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
||||||
protected abstract AttributeConversionInfo locateAttributeConversionInfo(String path);
|
protected abstract AttributeConversionInfo locateAttributeConversionInfo(String path);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AttributeConverterDefinition resolveAttributeConverterDefinition(XProperty property) {
|
public AttributeConverterDescriptor resolveAttributeConverterDescriptor(XProperty property) {
|
||||||
AttributeConversionInfo info = locateAttributeConversionInfo( property );
|
AttributeConversionInfo info = locateAttributeConversionInfo( property );
|
||||||
if ( info != null ) {
|
if ( info != null ) {
|
||||||
if ( info.isConversionDisabled() ) {
|
if ( info.isConversionDisabled() ) {
|
||||||
|
@ -76,7 +78,7 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
return makeAttributeConverterDefinition( info );
|
return makeAttributeConverterDescriptor( info );
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
throw new IllegalStateException(
|
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() );
|
log.debugf( "Attempting to locate auto-apply AttributeConverter for property [%s:%s]", path, property.getName() );
|
||||||
|
|
||||||
final Class propertyType = context.getBuildingOptions().getReflectionManager().toClass( property.getType() );
|
return context.getMetadataCollector()
|
||||||
for ( AttributeConverterDefinition attributeConverterDefinition : context.getMetadataCollector().getAttributeConverters() ) {
|
.getAttributeConverterAutoApplyHandler()
|
||||||
if ( ! attributeConverterDefinition.isAutoApply() ) {
|
.findAutoApplyConverterForAttribute( property, context );
|
||||||
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;
|
protected AttributeConverterDescriptor makeAttributeConverterDescriptor(AttributeConversionInfo conversion) {
|
||||||
}
|
|
||||||
|
|
||||||
protected AttributeConverterDefinition makeAttributeConverterDefinition(AttributeConversionInfo conversion) {
|
|
||||||
try {
|
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) {
|
catch (Exception e) {
|
||||||
throw new AnnotationException( "Unable to create AttributeConverter instance", e );
|
throw new AnnotationException( "Unable to create AttributeConverter instance", e );
|
||||||
|
|
|
@ -12,20 +12,20 @@ import java.lang.reflect.TypeVariable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.persistence.AttributeConverter;
|
import javax.persistence.AttributeConverter;
|
||||||
import javax.persistence.Converter;
|
import javax.persistence.Converter;
|
||||||
|
|
||||||
import org.hibernate.AnnotationException;
|
import org.hibernate.AnnotationException;
|
||||||
import org.hibernate.AssertionFailure;
|
import org.hibernate.AssertionFailure;
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Externalized representation of an AttributeConverter
|
||||||
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
*
|
||||||
|
* @see org.hibernate.boot.spi.AttributeConverterDescriptor
|
||||||
*/
|
*/
|
||||||
public class AttributeConverterDefinition {
|
public class AttributeConverterDefinition {
|
||||||
private static final Logger log = Logger.getLogger( AttributeConverterDefinition.class );
|
|
||||||
|
|
||||||
private final AttributeConverter attributeConverter;
|
private final AttributeConverter attributeConverter;
|
||||||
private final boolean autoApply;
|
private final boolean autoApply;
|
||||||
private final Class entityAttributeType;
|
private final Class entityAttributeType;
|
||||||
|
|
|
@ -25,7 +25,7 @@ import org.hibernate.annotations.ManyToAny;
|
||||||
import org.hibernate.annotations.MapKeyType;
|
import org.hibernate.annotations.MapKeyType;
|
||||||
import org.hibernate.annotations.common.reflection.XClass;
|
import org.hibernate.annotations.common.reflection.XClass;
|
||||||
import org.hibernate.annotations.common.reflection.XProperty;
|
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.boot.spi.MetadataBuildingContext;
|
||||||
import org.hibernate.internal.CoreLogging;
|
import org.hibernate.internal.CoreLogging;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
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" );
|
AttributeConversionInfo info = locateAttributeConversionInfo( "element" );
|
||||||
if ( info != null ) {
|
if ( info != null ) {
|
||||||
if ( info.isConversionDisabled() ) {
|
if ( info.isConversionDisabled() ) {
|
||||||
|
@ -353,7 +353,7 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
return makeAttributeConverterDefinition( info );
|
return makeAttributeConverterDescriptor( info );
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
|
@ -369,25 +369,11 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
|
||||||
collection.getRole()
|
collection.getRole()
|
||||||
);
|
);
|
||||||
|
|
||||||
final Class elementClass = determineElementClass( elementXClass );
|
// todo : do we need to pass along `XClass 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return getContext().getMetadataCollector()
|
||||||
|
.getAttributeConverterAutoApplyHandler()
|
||||||
|
.findAutoApplyConverterForCollectionElement( collectionXProperty, getContext() );
|
||||||
}
|
}
|
||||||
|
|
||||||
private Class determineElementClass(XClass elementXClass) {
|
private Class determineElementClass(XClass elementXClass) {
|
||||||
|
@ -419,7 +405,7 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AttributeConverterDefinition keyElementAttributeConverterDefinition(XClass keyXClass) {
|
public AttributeConverterDescriptor mapKeyAttributeConverterDescriptor(XProperty mapXProperty, XClass keyXClass) {
|
||||||
AttributeConversionInfo info = locateAttributeConversionInfo( "key" );
|
AttributeConversionInfo info = locateAttributeConversionInfo( "key" );
|
||||||
if ( info != null ) {
|
if ( info != null ) {
|
||||||
if ( info.isConversionDisabled() ) {
|
if ( info.isConversionDisabled() ) {
|
||||||
|
@ -427,7 +413,7 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
return makeAttributeConverterDefinition( info );
|
return makeAttributeConverterDescriptor( info );
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
|
@ -443,25 +429,11 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
|
||||||
collection.getRole()
|
collection.getRole()
|
||||||
);
|
);
|
||||||
|
|
||||||
final Class elementClass = determineKeyClass( keyXClass );
|
// todo : do we need to pass along `XClass 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return getContext().getMetadataCollector()
|
||||||
|
.getAttributeConverterAutoApplyHandler()
|
||||||
|
.findAutoApplyConverterForMapKey( mapXProperty, getContext() );
|
||||||
}
|
}
|
||||||
|
|
||||||
private Class determineKeyClass(XClass keyXClass) {
|
private Class determineKeyClass(XClass keyXClass) {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import javax.persistence.JoinTable;
|
||||||
|
|
||||||
import org.hibernate.annotations.common.reflection.XClass;
|
import org.hibernate.annotations.common.reflection.XClass;
|
||||||
import org.hibernate.annotations.common.reflection.XProperty;
|
import org.hibernate.annotations.common.reflection.XProperty;
|
||||||
|
import org.hibernate.boot.spi.AttributeConverterDescriptor;
|
||||||
import org.hibernate.mapping.Join;
|
import org.hibernate.mapping.Join;
|
||||||
import org.hibernate.mapping.KeyValue;
|
import org.hibernate.mapping.KeyValue;
|
||||||
import org.hibernate.mapping.PersistentClass;
|
import org.hibernate.mapping.PersistentClass;
|
||||||
|
@ -91,5 +92,5 @@ public interface PropertyHolder {
|
||||||
* @param property
|
* @param property
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
AttributeConverterDefinition resolveAttributeConverterDefinition(XProperty property);
|
AttributeConverterDescriptor resolveAttributeConverterDescriptor(XProperty property);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1460,7 +1460,7 @@ public abstract class CollectionBinder {
|
||||||
property,
|
property,
|
||||||
elementClass,
|
elementClass,
|
||||||
collValue.getOwnerEntityName(),
|
collValue.getOwnerEntityName(),
|
||||||
holder.resolveElementAttributeConverterDefinition( elementClass )
|
holder.resolveElementAttributeConverterDescriptor( property, elementClass )
|
||||||
);
|
);
|
||||||
elementBinder.setPersistentClassName( propertyHolder.getEntityName() );
|
elementBinder.setPersistentClassName( propertyHolder.getEntityName() );
|
||||||
elementBinder.setAccessType( accessType );
|
elementBinder.setAccessType( accessType );
|
||||||
|
|
|
@ -275,7 +275,7 @@ public class MapBinder extends CollectionBinder {
|
||||||
property,
|
property,
|
||||||
keyXClass,
|
keyXClass,
|
||||||
this.collection.getOwnerEntityName(),
|
this.collection.getOwnerEntityName(),
|
||||||
holder.keyElementAttributeConverterDefinition( keyXClass )
|
holder.mapKeyAttributeConverterDescriptor( property, keyXClass )
|
||||||
);
|
);
|
||||||
elementBinder.setPersistentClassName( propertyHolder.getEntityName() );
|
elementBinder.setPersistentClassName( propertyHolder.getEntityName() );
|
||||||
elementBinder.setAccessType( accessType );
|
elementBinder.setAccessType( accessType );
|
||||||
|
|
|
@ -180,7 +180,7 @@ public class PropertyBinder {
|
||||||
property,
|
property,
|
||||||
returnedClass,
|
returnedClass,
|
||||||
containerClassName,
|
containerClassName,
|
||||||
holder.resolveAttributeConverterDefinition( property )
|
holder.resolveAttributeConverterDescriptor( property )
|
||||||
);
|
);
|
||||||
simpleValueBinder.setReferencedEntityName( referencedEntityName );
|
simpleValueBinder.setReferencedEntityName( referencedEntityName );
|
||||||
simpleValueBinder.setAccessType( accessType );
|
simpleValueBinder.setAccessType( accessType );
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.hibernate.annotations.common.reflection.XClass;
|
||||||
import org.hibernate.annotations.common.reflection.XProperty;
|
import org.hibernate.annotations.common.reflection.XProperty;
|
||||||
import org.hibernate.annotations.common.util.StandardClassLoaderDelegateImpl;
|
import org.hibernate.annotations.common.util.StandardClassLoaderDelegateImpl;
|
||||||
import org.hibernate.boot.model.TypeDefinition;
|
import org.hibernate.boot.model.TypeDefinition;
|
||||||
|
import org.hibernate.boot.spi.AttributeConverterDescriptor;
|
||||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||||
import org.hibernate.cfg.AccessType;
|
import org.hibernate.cfg.AccessType;
|
||||||
import org.hibernate.cfg.AttributeConverterDefinition;
|
import org.hibernate.cfg.AttributeConverterDefinition;
|
||||||
|
@ -86,7 +87,7 @@ public class SimpleValueBinder {
|
||||||
private XProperty xproperty;
|
private XProperty xproperty;
|
||||||
private AccessType accessType;
|
private AccessType accessType;
|
||||||
|
|
||||||
private AttributeConverterDefinition attributeConverterDefinition;
|
private AttributeConverterDescriptor attributeConverterDescriptor;
|
||||||
|
|
||||||
public void setReferencedEntityName(String referencedEntityName) {
|
public void setReferencedEntityName(String referencedEntityName) {
|
||||||
this.referencedEntityName = referencedEntityName;
|
this.referencedEntityName = referencedEntityName;
|
||||||
|
@ -131,7 +132,7 @@ public class SimpleValueBinder {
|
||||||
|
|
||||||
//TODO execute it lazily to be order safe
|
//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 ) {
|
if ( returnedClass == null ) {
|
||||||
// we cannot guess anything
|
// we cannot guess anything
|
||||||
return;
|
return;
|
||||||
|
@ -302,11 +303,11 @@ public class SimpleValueBinder {
|
||||||
defaultType = BinderHelper.isEmptyAnnotationValue( type ) ? returnedClassName : type;
|
defaultType = BinderHelper.isEmptyAnnotationValue( type ) ? returnedClassName : type;
|
||||||
this.typeParameters = typeParameters;
|
this.typeParameters = typeParameters;
|
||||||
|
|
||||||
applyAttributeConverter( property, attributeConverterDefinition );
|
applyAttributeConverter( property, attributeConverterDescriptor );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyAttributeConverter(XProperty property, AttributeConverterDefinition attributeConverterDefinition) {
|
private void applyAttributeConverter(XProperty property, AttributeConverterDescriptor attributeConverterDescriptor) {
|
||||||
if ( attributeConverterDefinition == null ) {
|
if ( attributeConverterDescriptor == null ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,7 +338,7 @@ public class SimpleValueBinder {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.attributeConverterDefinition = attributeConverterDefinition;
|
this.attributeConverterDescriptor = attributeConverterDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isAssociation() {
|
private boolean isAssociation() {
|
||||||
|
@ -429,7 +430,7 @@ public class SimpleValueBinder {
|
||||||
public void fillSimpleValue() {
|
public void fillSimpleValue() {
|
||||||
LOG.debugf( "Starting fillSimpleValue for %s", propertyName );
|
LOG.debugf( "Starting fillSimpleValue for %s", propertyName );
|
||||||
|
|
||||||
if ( attributeConverterDefinition != null ) {
|
if ( attributeConverterDescriptor != null ) {
|
||||||
if ( ! BinderHelper.isEmptyAnnotationValue( explicitType ) ) {
|
if ( ! BinderHelper.isEmptyAnnotationValue( explicitType ) ) {
|
||||||
throw new AnnotationException(
|
throw new AnnotationException(
|
||||||
String.format(
|
String.format(
|
||||||
|
@ -442,11 +443,11 @@ public class SimpleValueBinder {
|
||||||
}
|
}
|
||||||
LOG.debugf(
|
LOG.debugf(
|
||||||
"Applying JPA AttributeConverter [%s] to [%s:%s]",
|
"Applying JPA AttributeConverter [%s] to [%s:%s]",
|
||||||
attributeConverterDefinition,
|
attributeConverterDescriptor,
|
||||||
persistentClassName,
|
persistentClassName,
|
||||||
propertyName
|
propertyName
|
||||||
);
|
);
|
||||||
simpleValue.setJpaAttributeConverterDefinition( attributeConverterDefinition );
|
simpleValue.setJpaAttributeConverterDescriptor( attributeConverterDescriptor );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
String type;
|
String type;
|
||||||
|
@ -480,7 +481,7 @@ public class SimpleValueBinder {
|
||||||
simpleValue.setTypeName( type );
|
simpleValue.setTypeName( type );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( persistentClassName != null || attributeConverterDefinition != null ) {
|
if ( persistentClassName != null || attributeConverterDescriptor != null ) {
|
||||||
try {
|
try {
|
||||||
simpleValue.setTypeUsingReflection( persistentClassName, propertyName );
|
simpleValue.setTypeUsingReflection( persistentClassName, propertyName );
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -803,4 +804,29 @@ public final class StringHelper {
|
||||||
public static List<String> parseCommaSeparatedString(String incomingString) {
|
public static List<String> parseCommaSeparatedString(String incomingString) {
|
||||||
return Arrays.asList( incomingString.split( "\\s*,\\s*" ) );
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,11 @@ import javax.persistence.AttributeConverter;
|
||||||
import org.hibernate.FetchMode;
|
import org.hibernate.FetchMode;
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
import org.hibernate.annotations.common.reflection.XProperty;
|
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.ClassLoaderService;
|
||||||
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
|
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
|
||||||
|
import org.hibernate.boot.spi.AttributeConverterDescriptor;
|
||||||
import org.hibernate.boot.spi.MetadataImplementor;
|
import org.hibernate.boot.spi.MetadataImplementor;
|
||||||
import org.hibernate.cfg.AttributeConverterDefinition;
|
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||||
|
@ -75,7 +76,7 @@ public class SimpleValue implements KeyValue {
|
||||||
private boolean alternateUniqueKey;
|
private boolean alternateUniqueKey;
|
||||||
private boolean cascadeDeleteEnabled;
|
private boolean cascadeDeleteEnabled;
|
||||||
|
|
||||||
private AttributeConverterDefinition attributeConverterDefinition;
|
private AttributeConverterDescriptor attributeConverterDescriptor;
|
||||||
private Type type;
|
private Type type;
|
||||||
|
|
||||||
public SimpleValue(MetadataImplementor metadata) {
|
public SimpleValue(MetadataImplementor metadata) {
|
||||||
|
@ -155,7 +156,7 @@ public class SimpleValue implements KeyValue {
|
||||||
.getService( ClassLoaderService.class );
|
.getService( ClassLoaderService.class );
|
||||||
try {
|
try {
|
||||||
final Class<AttributeConverter> converterClass = cls.classForName( converterClassName );
|
final Class<AttributeConverter> converterClass = cls.classForName( converterClassName );
|
||||||
attributeConverterDefinition = new AttributeConverterDefinition( converterClass.newInstance(), false );
|
attributeConverterDescriptor = new AttributeConverterDescriptorNonAutoApplicableImpl( converterClass.newInstance() );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
|
@ -420,7 +421,7 @@ public class SimpleValue implements KeyValue {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( attributeConverterDefinition == null ) {
|
if ( attributeConverterDescriptor == null ) {
|
||||||
// this is here to work like legacy. This should change when we integrate with metamodel to
|
// 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
|
// look for SqlTypeDescriptor and JavaTypeDescriptor individually and create the BasicType (well, really
|
||||||
// keep a registry of [SqlTypeDescriptor,JavaTypeDescriptor] -> BasicType...)
|
// keep a registry of [SqlTypeDescriptor,JavaTypeDescriptor] -> BasicType...)
|
||||||
|
@ -475,8 +476,8 @@ public class SimpleValue implements KeyValue {
|
||||||
private Type buildAttributeConverterTypeAdapter() {
|
private Type buildAttributeConverterTypeAdapter() {
|
||||||
// todo : validate the number of columns present here?
|
// todo : validate the number of columns present here?
|
||||||
|
|
||||||
final Class entityAttributeJavaType = attributeConverterDefinition.getEntityAttributeType();
|
final Class entityAttributeJavaType = attributeConverterDescriptor.getDomainType();
|
||||||
final Class databaseColumnJavaType = attributeConverterDefinition.getDatabaseColumnType();
|
final Class databaseColumnJavaType = attributeConverterDescriptor.getJdbcType();
|
||||||
|
|
||||||
|
|
||||||
// resolve the JavaTypeDescriptor ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// 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
|
// and finally construct the adapter, which injects the AttributeConverter calls into the binding/extraction
|
||||||
// process...
|
// process...
|
||||||
final SqlTypeDescriptor sqlTypeDescriptorAdapter = new AttributeConverterSqlTypeDescriptorAdapter(
|
final SqlTypeDescriptor sqlTypeDescriptorAdapter = new AttributeConverterSqlTypeDescriptorAdapter(
|
||||||
attributeConverterDefinition.getAttributeConverter(),
|
attributeConverterDescriptor.getAttributeConverter(),
|
||||||
sqlTypeDescriptor,
|
sqlTypeDescriptor,
|
||||||
intermediateJavaTypeDescriptor
|
intermediateJavaTypeDescriptor
|
||||||
);
|
);
|
||||||
|
|
||||||
// todo : cache the AttributeConverterTypeAdapter in case that AttributeConverter is applied multiple times.
|
// 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(
|
final String description = String.format(
|
||||||
"BasicType adapter for AttributeConverter<%s,%s>",
|
"BasicType adapter for AttributeConverter<%s,%s>",
|
||||||
entityAttributeJavaType.getSimpleName(),
|
entityAttributeJavaType.getSimpleName(),
|
||||||
|
@ -539,7 +540,7 @@ public class SimpleValue implements KeyValue {
|
||||||
return new AttributeConverterTypeAdapter(
|
return new AttributeConverterTypeAdapter(
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
attributeConverterDefinition.getAttributeConverter(),
|
attributeConverterDescriptor.getAttributeConverter(),
|
||||||
sqlTypeDescriptorAdapter,
|
sqlTypeDescriptorAdapter,
|
||||||
entityAttributeJavaType,
|
entityAttributeJavaType,
|
||||||
databaseColumnJavaType,
|
databaseColumnJavaType,
|
||||||
|
@ -583,8 +584,8 @@ public class SimpleValue implements KeyValue {
|
||||||
return getColumnInsertability();
|
return getColumnInsertability();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setJpaAttributeConverterDefinition(AttributeConverterDefinition attributeConverterDefinition) {
|
public void setJpaAttributeConverterDescriptor(AttributeConverterDescriptor attributeConverterDescriptor) {
|
||||||
this.attributeConverterDefinition = attributeConverterDefinition;
|
this.attributeConverterDescriptor = attributeConverterDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createParameterImpl() {
|
private void createParameterImpl() {
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.hibernate.IrrelevantEntity;
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
import org.hibernate.SessionFactory;
|
import org.hibernate.SessionFactory;
|
||||||
import org.hibernate.boot.MetadataSources;
|
import org.hibernate.boot.MetadataSources;
|
||||||
|
import org.hibernate.boot.internal.AttributeConverterDescriptorNonAutoApplicableImpl;
|
||||||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||||
import org.hibernate.boot.spi.MetadataImplementor;
|
import org.hibernate.boot.spi.MetadataImplementor;
|
||||||
|
@ -96,8 +97,8 @@ public class AttributeConverterTest extends BaseUnitTestCase {
|
||||||
try {
|
try {
|
||||||
MetadataImplementor metadata = (MetadataImplementor) new MetadataSources( ssr ).buildMetadata();
|
MetadataImplementor metadata = (MetadataImplementor) new MetadataSources( ssr ).buildMetadata();
|
||||||
SimpleValue simpleValue = new SimpleValue( metadata );
|
SimpleValue simpleValue = new SimpleValue( metadata );
|
||||||
simpleValue.setJpaAttributeConverterDefinition(
|
simpleValue.setJpaAttributeConverterDescriptor(
|
||||||
new AttributeConverterDefinition( new StringClobConverter(), true )
|
new AttributeConverterDescriptorNonAutoApplicableImpl( new StringClobConverter() )
|
||||||
);
|
);
|
||||||
simpleValue.setTypeUsingReflection( IrrelevantEntity.class.getName(), "name" );
|
simpleValue.setTypeUsingReflection( IrrelevantEntity.class.getName(), "name" );
|
||||||
|
|
||||||
|
|
|
@ -8,14 +8,29 @@ package org.hibernate.test.converter.generics;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
import javax.persistence.AttributeConverter;
|
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.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.TestForIssue;
|
||||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,6 +42,20 @@ import static org.junit.Assert.assertEquals;
|
||||||
*/
|
*/
|
||||||
public class ParameterizedAttributeConverterParameterTypeTest extends BaseUnitTestCase {
|
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> {
|
public static class CustomAttributeConverter implements AttributeConverter<List<String>, Integer> {
|
||||||
@Override
|
@Override
|
||||||
public Integer convertToDatabaseColumn(List<String> attribute) {
|
public Integer convertToDatabaseColumn(List<String> attribute) {
|
||||||
|
@ -46,4 +75,119 @@ public class ParameterizedAttributeConverterParameterTypeTest extends BaseUnitTe
|
||||||
assertEquals( List.class, def.getEntityAttributeType() );
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ ext {
|
||||||
// Annotations
|
// Annotations
|
||||||
commons_annotations: 'org.hibernate.common:hibernate-commons-annotations:5.0.0.Final',
|
commons_annotations: 'org.hibernate.common:hibernate-commons-annotations:5.0.0.Final',
|
||||||
jandex: 'org.jboss:jandex:2.0.0.CR1',
|
jandex: 'org.jboss:jandex:2.0.0.CR1',
|
||||||
classmate: 'com.fasterxml:classmate:0.8.0',
|
classmate: 'com.fasterxml:classmate:1.3.0',
|
||||||
|
|
||||||
// Woodstox
|
// Woodstox
|
||||||
woodstox: "org.codehaus.woodstox:woodstox-core-asl:4.3.0",
|
woodstox: "org.codehaus.woodstox:woodstox-core-asl:4.3.0",
|
||||||
|
|
Loading…
Reference in New Issue