HHH-8469 - Application of JPA 2.1 AttributeConverters
This commit is contained in:
parent
cf7bb0dcec
commit
1f6daa6785
|
@ -22,8 +22,7 @@
|
|||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.cfg;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.AssociationOverride;
|
||||
import javax.persistence.AssociationOverrides;
|
||||
import javax.persistence.AttributeOverride;
|
||||
|
@ -34,11 +33,17 @@ import javax.persistence.Entity;
|
|||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.AnnotationException;
|
||||
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.internal.CoreLogging;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
|
||||
/**
|
||||
|
@ -47,6 +52,8 @@ import org.hibernate.internal.util.StringHelper;
|
|||
* @author Emmanuel Bernard
|
||||
*/
|
||||
public abstract class AbstractPropertyHolder implements PropertyHolder {
|
||||
private static final Logger log = CoreLogging.logger( AbstractPropertyHolder.class );
|
||||
|
||||
protected AbstractPropertyHolder parent;
|
||||
private Map<String, Column[]> holderColumnOverride;
|
||||
private Map<String, Column[]> currentPropertyColumnOverride;
|
||||
|
@ -58,7 +65,6 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
private Mappings mappings;
|
||||
private Boolean isInIdClass;
|
||||
|
||||
|
||||
AbstractPropertyHolder(
|
||||
String path,
|
||||
PropertyHolder parent,
|
||||
|
@ -70,6 +76,122 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
|
|||
buildHierarchyColumnOverride( clazzToProcess );
|
||||
}
|
||||
|
||||
protected abstract String normalizeCompositePathForLogging(String attributeName);
|
||||
protected abstract String normalizeCompositePath(String attributeName);
|
||||
|
||||
protected abstract AttributeConversionInfo locateAttributeConversionInfo(XProperty property);
|
||||
protected abstract AttributeConversionInfo locateAttributeConversionInfo(String path);
|
||||
|
||||
@Override
|
||||
public AttributeConverterDefinition resolveAttributeConverterDefinition(XProperty property) {
|
||||
AttributeConversionInfo info = locateAttributeConversionInfo( property );
|
||||
if ( info != null ) {
|
||||
if ( info.isConversionDisabled() ) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
try {
|
||||
return makeAttributeConverterDefinition( info );
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException(
|
||||
String.format( "Unable to instantiate AttributeConverter [%s", info.getConverterClass().getName() ),
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// look for auto applied converters
|
||||
// todo : hook in the orm.xml local entity work brett did
|
||||
// for now, just look in global converters
|
||||
|
||||
|
||||
log.debugf( "Attempting to locate auto-apply AttributeConverter for property [%s:%s]", path, property.getName() );
|
||||
|
||||
final Class propertyType = mappings.getReflectionManager().toClass( property.getType() );
|
||||
for ( AttributeConverterDefinition attributeConverterDefinition : mappings.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;
|
||||
}
|
||||
|
||||
private AttributeConverterDefinition makeAttributeConverterDefinition(AttributeConversionInfo conversion) {
|
||||
try {
|
||||
return new AttributeConverterDefinition( conversion.getConverterClass().newInstance(), false );
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new AnnotationException( "Unable to create AttributeConverter instance", e );
|
||||
}
|
||||
}
|
||||
|
||||
private boolean areTypeMatch(Class converterDefinedType, Class propertyType) {
|
||||
if ( converterDefinedType == null ) {
|
||||
throw new AnnotationException( "AttributeConverter defined java type cannot be null" );
|
||||
}
|
||||
if ( propertyType == null ) {
|
||||
throw new AnnotationException( "Property defined java type cannot be null" );
|
||||
}
|
||||
|
||||
return converterDefinedType.equals( propertyType )
|
||||
|| arePrimitiveWrapperEquivalents( converterDefinedType, propertyType );
|
||||
}
|
||||
|
||||
private boolean arePrimitiveWrapperEquivalents(Class converterDefinedType, Class propertyType) {
|
||||
if ( converterDefinedType.isPrimitive() ) {
|
||||
return getWrapperEquivalent( converterDefinedType ).equals( propertyType );
|
||||
}
|
||||
else if ( propertyType.isPrimitive() ) {
|
||||
return getWrapperEquivalent( propertyType ).equals( converterDefinedType );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Class getWrapperEquivalent(Class primitive) {
|
||||
if ( ! primitive.isPrimitive() ) {
|
||||
throw new AssertionFailure( "Passed type for which to locate wrapper equivalent was not a primitive" );
|
||||
}
|
||||
|
||||
if ( boolean.class.equals( primitive ) ) {
|
||||
return Boolean.class;
|
||||
}
|
||||
else if ( char.class.equals( primitive ) ) {
|
||||
return Character.class;
|
||||
}
|
||||
else if ( byte.class.equals( primitive ) ) {
|
||||
return Byte.class;
|
||||
}
|
||||
else if ( short.class.equals( primitive ) ) {
|
||||
return Short.class;
|
||||
}
|
||||
else if ( int.class.equals( primitive ) ) {
|
||||
return Integer.class;
|
||||
}
|
||||
else if ( long.class.equals( primitive ) ) {
|
||||
return Long.class;
|
||||
}
|
||||
else if ( float.class.equals( primitive ) ) {
|
||||
return Float.class;
|
||||
}
|
||||
else if ( double.class.equals( primitive ) ) {
|
||||
return Double.class;
|
||||
}
|
||||
|
||||
throw new AssertionFailure( "Unexpected primitive type (VOID most likely) passed to getWrapperEquivalent" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInIdClass() {
|
||||
return isInIdClass != null ? isInIdClass : parent != null ? parent.isInIdClass() : false;
|
||||
|
|
|
@ -135,6 +135,8 @@ import org.hibernate.annotations.Tuplizers;
|
|||
import org.hibernate.annotations.TypeDef;
|
||||
import org.hibernate.annotations.TypeDefs;
|
||||
import org.hibernate.annotations.Where;
|
||||
import org.hibernate.annotations.common.reflection.ClassLoaderDelegate;
|
||||
import org.hibernate.annotations.common.reflection.ClassLoadingException;
|
||||
import org.hibernate.annotations.common.reflection.ReflectionManager;
|
||||
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
|
||||
import org.hibernate.annotations.common.reflection.XClass;
|
||||
|
@ -292,6 +294,10 @@ public final class AnnotationBinder {
|
|||
try {
|
||||
pckg = mappings.getReflectionManager().packageForName( packageName );
|
||||
}
|
||||
catch (ClassLoadingException e) {
|
||||
LOG.packageNotFound( packageName );
|
||||
return;
|
||||
}
|
||||
catch ( ClassNotFoundException cnf ) {
|
||||
LOG.packageNotFound( packageName );
|
||||
return;
|
||||
|
@ -2415,10 +2421,18 @@ public final class AnnotationBinder {
|
|||
String subpath = BinderHelper.getPath( propertyHolder, inferredData );
|
||||
LOG.tracev( "Binding component with path: {0}", subpath );
|
||||
PropertyHolder subHolder = PropertyHolderBuilder.buildPropertyHolder(
|
||||
comp, subpath,
|
||||
inferredData, propertyHolder, mappings
|
||||
comp,
|
||||
subpath,
|
||||
inferredData,
|
||||
propertyHolder,
|
||||
mappings
|
||||
);
|
||||
|
||||
|
||||
// propertyHolder here is the owner of the component property. Tell it we are about to start the component...
|
||||
|
||||
propertyHolder.startingProperty( inferredData.getProperty() );
|
||||
|
||||
final XClass xClassProcessed = inferredData.getPropertyClass();
|
||||
List<PropertyData> classElements = new ArrayList<PropertyData>();
|
||||
XClass returnedClassOrElement = inferredData.getClassOrElement();
|
||||
|
@ -2599,7 +2613,7 @@ public final class AnnotationBinder {
|
|||
value.setColumns( columns );
|
||||
value.setPersistentClassName( persistentClassName );
|
||||
value.setMappings( mappings );
|
||||
value.setType( inferredData.getProperty(), inferredData.getClassOrElement(), persistentClassName );
|
||||
value.setType( inferredData.getProperty(), inferredData.getClassOrElement(), persistentClassName, null );
|
||||
value.setAccessType( propertyAccessor );
|
||||
id = value.make();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.cfg;
|
||||
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Convert;
|
||||
|
||||
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
|
||||
|
||||
/**
|
||||
* Describes a {@link javax.persistence.Convert} conversion
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class AttributeConversionInfo {
|
||||
private final Class<? extends AttributeConverter> converterClass;
|
||||
private final boolean conversionDisabled;
|
||||
|
||||
private final String attributeName;
|
||||
|
||||
private final XAnnotatedElement source;
|
||||
|
||||
public AttributeConversionInfo(
|
||||
Class<? extends AttributeConverter> converterClass,
|
||||
boolean conversionDisabled,
|
||||
String attributeName,
|
||||
XAnnotatedElement source) {
|
||||
this.converterClass = converterClass;
|
||||
this.conversionDisabled = conversionDisabled;
|
||||
this.attributeName = attributeName;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public AttributeConversionInfo(Convert convertAnnotation, XAnnotatedElement xAnnotatedElement) {
|
||||
this(
|
||||
convertAnnotation.converter(),
|
||||
convertAnnotation.disableConversion(),
|
||||
convertAnnotation.attributeName(),
|
||||
xAnnotatedElement
|
||||
);
|
||||
}
|
||||
|
||||
public Class<? extends AttributeConverter> getConverterClass() {
|
||||
return converterClass;
|
||||
}
|
||||
|
||||
public boolean isConversionDisabled() {
|
||||
return conversionDisabled;
|
||||
}
|
||||
|
||||
public String getAttributeName() {
|
||||
return attributeName;
|
||||
}
|
||||
|
||||
public XAnnotatedElement getSource() {
|
||||
return source;
|
||||
}
|
||||
}
|
|
@ -22,21 +22,21 @@
|
|||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.cfg;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.Convert;
|
||||
import javax.persistence.Converts;
|
||||
import javax.persistence.JoinTable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.annotations.common.AssertionFailure;
|
||||
import org.hibernate.annotations.common.reflection.XClass;
|
||||
import org.hibernate.annotations.common.reflection.XProperty;
|
||||
import org.hibernate.annotations.common.util.ReflectHelper;
|
||||
import org.hibernate.cfg.annotations.EntityBinder;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.mapping.Component;
|
||||
import org.hibernate.mapping.Join;
|
||||
import org.hibernate.mapping.KeyValue;
|
||||
|
@ -49,36 +49,145 @@ import org.hibernate.mapping.Table;
|
|||
* @author Emmanuel Bernard
|
||||
*/
|
||||
public class ClassPropertyHolder extends AbstractPropertyHolder {
|
||||
private static final Logger log = CoreLogging.logger( ClassPropertyHolder.class );
|
||||
|
||||
private PersistentClass persistentClass;
|
||||
private Map<String, Join> joins;
|
||||
private transient Map<String, Join> joinsPerRealTableName;
|
||||
private EntityBinder entityBinder;
|
||||
private final Map<XClass, InheritanceState> inheritanceStatePerClass;
|
||||
|
||||
private Map<String,AttributeConversionInfo> attributeConversionInfoMap;
|
||||
|
||||
public ClassPropertyHolder(
|
||||
PersistentClass persistentClass,
|
||||
XClass clazzToProcess,
|
||||
XClass entityXClass,
|
||||
Map<String, Join> joins,
|
||||
Mappings mappings,
|
||||
Map<XClass, InheritanceState> inheritanceStatePerClass) {
|
||||
super( persistentClass.getEntityName(), null, clazzToProcess, mappings );
|
||||
super( persistentClass.getEntityName(), null, entityXClass, mappings );
|
||||
this.persistentClass = persistentClass;
|
||||
this.joins = joins;
|
||||
this.inheritanceStatePerClass = inheritanceStatePerClass;
|
||||
|
||||
this.attributeConversionInfoMap = buildAttributeConversionInfoMap( entityXClass );
|
||||
}
|
||||
|
||||
public ClassPropertyHolder(
|
||||
PersistentClass persistentClass,
|
||||
XClass clazzToProcess,
|
||||
XClass entityXClass,
|
||||
EntityBinder entityBinder,
|
||||
Mappings mappings,
|
||||
Map<XClass, InheritanceState> inheritanceStatePerClass) {
|
||||
this( persistentClass, clazzToProcess, entityBinder.getSecondaryTables(), mappings, inheritanceStatePerClass );
|
||||
this( persistentClass, entityXClass, entityBinder.getSecondaryTables(), mappings, inheritanceStatePerClass );
|
||||
this.entityBinder = entityBinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String normalizeCompositePath(String attributeName) {
|
||||
return attributeName;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String normalizeCompositePathForLogging(String attributeName) {
|
||||
return getEntityName() + '.' + attributeName;
|
||||
}
|
||||
|
||||
protected Map<String, AttributeConversionInfo> buildAttributeConversionInfoMap(XClass entityXClass) {
|
||||
final HashMap<String, AttributeConversionInfo> map = new HashMap<String, AttributeConversionInfo>();
|
||||
collectAttributeConversionInfo( map, entityXClass );
|
||||
return map;
|
||||
}
|
||||
|
||||
private void collectAttributeConversionInfo(Map<String, AttributeConversionInfo> infoMap, XClass xClass) {
|
||||
if ( xClass == null ) {
|
||||
// typically indicates we have reached the end of the inheritance hierarchy
|
||||
return;
|
||||
}
|
||||
|
||||
// collect superclass info first
|
||||
collectAttributeConversionInfo( infoMap, xClass.getSuperclass() );
|
||||
|
||||
final boolean canContainConvert = xClass.isAnnotationPresent( javax.persistence.Entity.class )
|
||||
|| xClass.isAnnotationPresent( javax.persistence.MappedSuperclass.class )
|
||||
|| xClass.isAnnotationPresent( javax.persistence.Embeddable.class );
|
||||
if ( ! canContainConvert ) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
final Convert convertAnnotation = xClass.getAnnotation( Convert.class );
|
||||
if ( convertAnnotation != null ) {
|
||||
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, xClass );
|
||||
if ( StringHelper.isEmpty( info.getAttributeName() ) ) {
|
||||
throw new IllegalStateException( "@Convert placed on @Entity/@MappedSuperclass must define attributeName" );
|
||||
}
|
||||
infoMap.put( info.getAttributeName(), info );
|
||||
}
|
||||
}
|
||||
{
|
||||
final Converts convertsAnnotation = xClass.getAnnotation( Converts.class );
|
||||
if ( convertsAnnotation != null ) {
|
||||
for ( Convert convertAnnotation : convertsAnnotation.value() ) {
|
||||
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, xClass );
|
||||
if ( StringHelper.isEmpty( info.getAttributeName() ) ) {
|
||||
throw new IllegalStateException( "@Converts placed on @Entity/@MappedSuperclass must define attributeName" );
|
||||
}
|
||||
infoMap.put( info.getAttributeName(), info );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startingProperty(XProperty property) {
|
||||
if ( property == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String propertyName = property.getName();
|
||||
if ( attributeConversionInfoMap.containsKey( propertyName ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
// @Convert annotation on the Embeddable attribute
|
||||
final Convert convertAnnotation = property.getAnnotation( Convert.class );
|
||||
if ( convertAnnotation != null ) {
|
||||
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, property );
|
||||
if ( StringHelper.isEmpty( info.getAttributeName() ) ) {
|
||||
attributeConversionInfoMap.put( propertyName, info );
|
||||
}
|
||||
else {
|
||||
attributeConversionInfoMap.put( propertyName + '.' + info.getAttributeName(), info );
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
// @Converts annotation on the Embeddable attribute
|
||||
final Converts convertsAnnotation = property.getAnnotation( Converts.class );
|
||||
if ( convertsAnnotation != null ) {
|
||||
for ( Convert convertAnnotation : convertsAnnotation.value() ) {
|
||||
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, property );
|
||||
if ( StringHelper.isEmpty( info.getAttributeName() ) ) {
|
||||
attributeConversionInfoMap.put( propertyName, info );
|
||||
}
|
||||
else {
|
||||
attributeConversionInfoMap.put( propertyName + '.' + info.getAttributeName(), info );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AttributeConversionInfo locateAttributeConversionInfo(XProperty property) {
|
||||
return locateAttributeConversionInfo( property.getName() );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AttributeConversionInfo locateAttributeConversionInfo(String path) {
|
||||
return attributeConversionInfoMap.get( path );
|
||||
}
|
||||
|
||||
public String getEntityName() {
|
||||
return persistentClass.getEntityName();
|
||||
}
|
||||
|
@ -286,4 +395,10 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
|
|||
//
|
||||
// return lookForEntityDefinedConvertAnnotation( property, owner );
|
||||
// }
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "(" + getEntityName() + ")";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.cfg;
|
||||
|
||||
import javax.persistence.JoinTable;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
|
@ -52,6 +53,33 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
|
|||
setCurrentProperty( property );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String normalizeCompositePath(String attributeName) {
|
||||
return attributeName;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String normalizeCompositePathForLogging(String attributeName) {
|
||||
return collection.getRole() + '.' + attributeName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startingProperty(XProperty property) {
|
||||
// todo : implement
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AttributeConversionInfo locateAttributeConversionInfo(XProperty property) {
|
||||
// todo : implement
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AttributeConversionInfo locateAttributeConversionInfo(String path) {
|
||||
// todo : implement
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
throw new AssertionFailure( "Collection property holder does not have a class name" );
|
||||
}
|
||||
|
@ -100,4 +128,9 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
|
|||
public Join addJoin(JoinTable joinTableAnn, boolean noDelayInPkColumnCreation) {
|
||||
throw new AssertionFailure( "Add a <join> in a second pass" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "(" + collection.getRole() + ")";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,14 +23,21 @@
|
|||
*/
|
||||
package org.hibernate.cfg;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Convert;
|
||||
import javax.persistence.Converts;
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.annotations.common.reflection.XClass;
|
||||
import org.hibernate.annotations.common.reflection.XProperty;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.mapping.Component;
|
||||
import org.hibernate.mapping.Join;
|
||||
import org.hibernate.mapping.KeyValue;
|
||||
|
@ -39,8 +46,31 @@ import org.hibernate.mapping.Property;
|
|||
import org.hibernate.mapping.Table;
|
||||
|
||||
/**
|
||||
* Component implementation of property holder
|
||||
* PropertyHolder for composites (Embeddable/Embedded).
|
||||
* <p/>
|
||||
* To facilitate code comments, I'll often refer to this example:
|
||||
* <pre>
|
||||
* @Embeddable
|
||||
* @Convert( attributeName="city", ... )
|
||||
* class Address {
|
||||
* ...
|
||||
* @Convert(...)
|
||||
* public String city;
|
||||
* }
|
||||
*
|
||||
* @Entity
|
||||
* @Convert( attributeName="homeAddress.city", ... )
|
||||
* class Person {
|
||||
* ...
|
||||
* @Embedded
|
||||
* @Convert( attributeName="city", ... )
|
||||
* public Address homeAddress;
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* As you can see, lots of ways to specify the conversion for embeddable attributes :(
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
public class ComponentPropertyHolder extends AbstractPropertyHolder {
|
||||
|
@ -49,6 +79,201 @@ public class ComponentPropertyHolder extends AbstractPropertyHolder {
|
|||
private Component component;
|
||||
private boolean isOrWithinEmbeddedId;
|
||||
|
||||
private boolean virtual;
|
||||
private String embeddedAttributeName;
|
||||
private Map<String,AttributeConversionInfo> attributeConversionInfoMap;
|
||||
|
||||
public ComponentPropertyHolder(
|
||||
Component component,
|
||||
String path,
|
||||
PropertyData inferredData,
|
||||
PropertyHolder parent,
|
||||
Mappings mappings) {
|
||||
super( path, parent, inferredData.getPropertyClass(), mappings );
|
||||
final XProperty embeddedXProperty = inferredData.getProperty();
|
||||
setCurrentProperty( embeddedXProperty );
|
||||
this.component = component;
|
||||
this.isOrWithinEmbeddedId =
|
||||
parent.isOrWithinEmbeddedId()
|
||||
|| ( embeddedXProperty != null &&
|
||||
( embeddedXProperty.isAnnotationPresent( Id.class )
|
||||
|| embeddedXProperty.isAnnotationPresent( EmbeddedId.class ) ) );
|
||||
|
||||
this.virtual = embeddedXProperty == null;
|
||||
if ( !virtual ) {
|
||||
this.embeddedAttributeName = embeddedXProperty.getName();
|
||||
this.attributeConversionInfoMap = processAttributeConversions( embeddedXProperty );
|
||||
}
|
||||
else {
|
||||
embeddedAttributeName = "";
|
||||
this.attributeConversionInfoMap = Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called from our constructor and handles (in order):<ol>
|
||||
* <li>@Convert annotation at the Embeddable class level</li>
|
||||
* <li>@Converts annotation at the Embeddable class level</li>
|
||||
* <li>@Convert annotation at the Embedded attribute level</li>
|
||||
* <li>@Converts annotation at the Embedded attribute level</li>
|
||||
* </ol>
|
||||
* <p/>
|
||||
* The order is important to ensure proper precedence.
|
||||
* <p/>
|
||||
* {@literal @Convert/@Converts} annotations at the Embeddable attribute level are handled in the calls to
|
||||
* {@link #startingProperty}. Duplicates are simply ignored there.
|
||||
*
|
||||
* @param embeddedXProperty The property that is the composite being described by this ComponentPropertyHolder
|
||||
*/
|
||||
private Map<String,AttributeConversionInfo> processAttributeConversions(XProperty embeddedXProperty) {
|
||||
final Map<String,AttributeConversionInfo> infoMap = new HashMap<String, AttributeConversionInfo>();
|
||||
|
||||
final XClass embeddableXClass = embeddedXProperty.getType();
|
||||
|
||||
// as a baseline, we want to apply conversions from the Embeddable and then overlay conversions
|
||||
// from the Embedded
|
||||
|
||||
// first apply conversions from the Embeddable...
|
||||
{
|
||||
// @Convert annotation on the Embeddable class level
|
||||
final Convert convertAnnotation = embeddableXClass.getAnnotation( Convert.class );
|
||||
if ( convertAnnotation != null ) {
|
||||
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, embeddableXClass );
|
||||
if ( StringHelper.isEmpty( info.getAttributeName() ) ) {
|
||||
throw new IllegalStateException( "@Convert placed on @Embeddable must define attributeName" );
|
||||
}
|
||||
infoMap.put( info.getAttributeName(), info );
|
||||
}
|
||||
}
|
||||
{
|
||||
// @Converts annotation on the Embeddable class level
|
||||
final Converts convertsAnnotation = embeddableXClass.getAnnotation( Converts.class );
|
||||
if ( convertsAnnotation != null ) {
|
||||
for ( Convert convertAnnotation : convertsAnnotation.value() ) {
|
||||
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, embeddableXClass );
|
||||
if ( StringHelper.isEmpty( info.getAttributeName() ) ) {
|
||||
throw new IllegalStateException( "@Converts placed on @Embeddable must define attributeName" );
|
||||
}
|
||||
infoMap.put( info.getAttributeName(), info );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// then we can overlay any conversions from the Embedded attribute
|
||||
{
|
||||
// @Convert annotation on the Embedded attribute
|
||||
final Convert convertAnnotation = embeddedXProperty.getAnnotation( Convert.class );
|
||||
if ( convertAnnotation != null ) {
|
||||
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, embeddableXClass );
|
||||
if ( StringHelper.isEmpty( info.getAttributeName() ) ) {
|
||||
throw new IllegalStateException( "Convert placed on Embedded attribute must define (sub)attributeName" );
|
||||
}
|
||||
infoMap.put( info.getAttributeName(), info );
|
||||
}
|
||||
}
|
||||
{
|
||||
// @Converts annotation on the Embedded attribute
|
||||
final Converts convertsAnnotation = embeddedXProperty.getAnnotation( Converts.class );
|
||||
if ( convertsAnnotation != null ) {
|
||||
for ( Convert convertAnnotation : convertsAnnotation.value() ) {
|
||||
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, embeddableXClass );
|
||||
if ( StringHelper.isEmpty( info.getAttributeName() ) ) {
|
||||
throw new IllegalStateException( "Convert placed on Embedded attribute must define (sub)attributeName" );
|
||||
}
|
||||
infoMap.put( info.getAttributeName(), info );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return infoMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String normalizeCompositePath(String attributeName) {
|
||||
return embeddedAttributeName + '.' + attributeName;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String normalizeCompositePathForLogging(String attributeName) {
|
||||
return normalizeCompositePath( attributeName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startingProperty(XProperty property) {
|
||||
if ( property == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( virtual ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// again : the property coming in here *should* be the property on the embeddable (Address#city in the example),
|
||||
// so we just ignore it if there is already an existing conversion info for that path since they would have
|
||||
// precedence
|
||||
|
||||
// technically we should only do this for properties of "basic type"
|
||||
|
||||
final String path = embeddedAttributeName + '.' + property.getName();
|
||||
if ( attributeConversionInfoMap.containsKey( path ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
// @Convert annotation on the Embeddable attribute
|
||||
final Convert convertAnnotation = property.getAnnotation( Convert.class );
|
||||
if ( convertAnnotation != null ) {
|
||||
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, property );
|
||||
attributeConversionInfoMap.put( property.getName(), info );
|
||||
}
|
||||
}
|
||||
{
|
||||
// @Converts annotation on the Embeddable attribute
|
||||
final Converts convertsAnnotation = property.getAnnotation( Converts.class );
|
||||
if ( convertsAnnotation != null ) {
|
||||
for ( Convert convertAnnotation : convertsAnnotation.value() ) {
|
||||
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, property );
|
||||
attributeConversionInfoMap.put( property.getName(), info );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AttributeConversionInfo locateAttributeConversionInfo(XProperty property) {
|
||||
final String propertyName = property.getName();
|
||||
|
||||
// conversions on parent would have precedence
|
||||
AttributeConversionInfo conversion = locateAttributeConversionInfo( propertyName );
|
||||
if ( conversion != null ) {
|
||||
return conversion;
|
||||
}
|
||||
|
||||
|
||||
Convert localConvert = property.getAnnotation( Convert.class );
|
||||
if ( localConvert != null ) {
|
||||
return new AttributeConversionInfo( localConvert, property );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AttributeConversionInfo locateAttributeConversionInfo(String path) {
|
||||
final String embeddedPath = embeddedAttributeName + '.' + path;
|
||||
AttributeConversionInfo fromParent = parent.locateAttributeConversionInfo( embeddedPath );
|
||||
if ( fromParent != null ) {
|
||||
return fromParent;
|
||||
}
|
||||
|
||||
AttributeConversionInfo fromEmbedded = attributeConversionInfoMap.get( embeddedPath );
|
||||
if ( fromEmbedded != null ) {
|
||||
return fromEmbedded;
|
||||
}
|
||||
|
||||
return attributeConversionInfoMap.get( path );
|
||||
}
|
||||
|
||||
public String getEntityName() {
|
||||
return component.getComponentClassName();
|
||||
}
|
||||
|
@ -82,23 +307,6 @@ public class ComponentPropertyHolder extends AbstractPropertyHolder {
|
|||
|
||||
}
|
||||
|
||||
public ComponentPropertyHolder(
|
||||
Component component,
|
||||
String path,
|
||||
PropertyData inferredData,
|
||||
PropertyHolder parent,
|
||||
Mappings mappings) {
|
||||
super( path, parent, inferredData.getPropertyClass(), mappings );
|
||||
final XProperty property = inferredData.getProperty();
|
||||
setCurrentProperty( property );
|
||||
this.component = component;
|
||||
this.isOrWithinEmbeddedId =
|
||||
parent.isOrWithinEmbeddedId()
|
||||
|| ( property != null &&
|
||||
( property.isAnnotationPresent( Id.class )
|
||||
|| property.isAnnotationPresent( EmbeddedId.class ) ) );
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return component.getComponentClassName();
|
||||
}
|
||||
|
@ -173,4 +381,9 @@ public class ComponentPropertyHolder extends AbstractPropertyHolder {
|
|||
public JoinColumn[] getOverriddenJoinColumn(String propertyName) {
|
||||
return super.getOverriddenJoinColumn( propertyName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "(" + parent.normalizeCompositePathForLogging( embeddedAttributeName ) + ")";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.cfg;
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
|
@ -67,8 +68,6 @@ public interface PropertyHolder {
|
|||
|
||||
String getPath();
|
||||
|
||||
// public AttributeConverterDefinition resolveAttributeConverter(String attributeName);
|
||||
|
||||
/**
|
||||
* return null if the column is not overridden, or an array of column if true
|
||||
*/
|
||||
|
@ -94,4 +93,20 @@ public interface PropertyHolder {
|
|||
boolean isInIdClass();
|
||||
|
||||
void setInIdClass(Boolean isInIdClass);
|
||||
|
||||
/**
|
||||
* Called during binding to allow the PropertyHolder to inspect its discovered properties. Mainly
|
||||
* this is used in collecting attribute conversion declarations (via @Convert/@Converts).
|
||||
*
|
||||
* @param property The property
|
||||
*/
|
||||
void startingProperty(XProperty property);
|
||||
|
||||
/**
|
||||
* Determine the AttributeConverter to use for the given property.
|
||||
*
|
||||
* @param property
|
||||
* @return
|
||||
*/
|
||||
AttributeConverterDefinition resolveAttributeConverterDefinition(XProperty property);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.cfg.annotations;
|
||||
|
||||
import javax.persistence.AttributeConverter;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface AttributeConverterResolver {
|
||||
/**
|
||||
* Resolve the AttributeConverter to use for an attribute during binding. The assumption of the API
|
||||
* is that the path information is encoded into the AttributeConverterResolver instance itself to account
|
||||
* for all the different paths converters can be defined on.
|
||||
*
|
||||
* @return The AttributeConverter to use, or {@code null} if none.
|
||||
*/
|
||||
public AttributeConverter resolveAttributeConverter();
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.cfg.annotations;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.annotations.common.reflection.XClass;
|
||||
import org.hibernate.annotations.common.reflection.XProperty;
|
||||
import org.hibernate.cfg.Mappings;
|
||||
|
||||
/**
|
||||
* Manages the resolution of the AttributeConverter, if one, for a property (path).
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class AttributeConverterResolverContext {
|
||||
private final Mappings mappings;
|
||||
|
||||
public AttributeConverterResolverContext(Mappings mappings) {
|
||||
this.mappings = mappings;
|
||||
}
|
||||
|
||||
private Map<String,EntityPropertyPathSource> entityPropertyPathSourceMap;
|
||||
|
||||
public EntityPropertyPathSource resolveEntityPropertyPathSource(XClass entityClass) {
|
||||
EntityPropertyPathSource found = null;
|
||||
if ( entityPropertyPathSourceMap == null ) {
|
||||
entityPropertyPathSourceMap = new HashMap<String, EntityPropertyPathSource>();
|
||||
}
|
||||
else {
|
||||
found = entityPropertyPathSourceMap.get( entityClass.getName() );
|
||||
}
|
||||
|
||||
if ( found == null ) {
|
||||
found = new EntityPropertyPathSource( entityClass );
|
||||
entityPropertyPathSourceMap.put( entityClass.getName(), found );
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
private Map<String,CollectionPropertyPathSource> collectionPropertyPathSourceMap;
|
||||
|
||||
public CollectionPropertyPathSource resolveRootCollectionPropertyPathSource(XClass owner, XProperty property) {
|
||||
CollectionPropertyPathSource found = null;
|
||||
if ( collectionPropertyPathSourceMap == null ) {
|
||||
collectionPropertyPathSourceMap = new HashMap<String, CollectionPropertyPathSource>();
|
||||
}
|
||||
else {
|
||||
found = collectionPropertyPathSourceMap.get( owner.getName() );
|
||||
}
|
||||
|
||||
if ( found == null ) {
|
||||
final EntityPropertyPathSource ownerSource = resolveEntityPropertyPathSource( owner );
|
||||
found = new CollectionPropertyPathSource( ownerSource, property.getName() );
|
||||
collectionPropertyPathSourceMap.put( owner.getName(), found );
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
public static interface PropertyPathSource {
|
||||
public CompositePropertyPathSource makeComposite(String propertyName);
|
||||
public CollectionPropertyPathSource makeCollection(String propertyName);
|
||||
}
|
||||
|
||||
public static abstract class AbstractPropertyPathSource implements PropertyPathSource {
|
||||
protected abstract String normalize(String path);
|
||||
|
||||
private Map<String,CompositePropertyPathSource> compositePropertyPathSourceMap;
|
||||
|
||||
@Override
|
||||
public CompositePropertyPathSource makeComposite(String propertyName) {
|
||||
CompositePropertyPathSource found = null;
|
||||
if ( compositePropertyPathSourceMap == null ) {
|
||||
compositePropertyPathSourceMap = new HashMap<String, CompositePropertyPathSource>();
|
||||
}
|
||||
else {
|
||||
found = compositePropertyPathSourceMap.get( propertyName );
|
||||
}
|
||||
|
||||
if ( found == null ) {
|
||||
found = new CompositePropertyPathSource( this, propertyName );
|
||||
compositePropertyPathSourceMap.put( propertyName, found );
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
private Map<String,CollectionPropertyPathSource> collectionPropertyPathSourceMap;
|
||||
|
||||
@Override
|
||||
public CollectionPropertyPathSource makeCollection(String propertyName) {
|
||||
CollectionPropertyPathSource found = null;
|
||||
if ( collectionPropertyPathSourceMap == null ) {
|
||||
collectionPropertyPathSourceMap = new HashMap<String, CollectionPropertyPathSource>();
|
||||
}
|
||||
else {
|
||||
found = collectionPropertyPathSourceMap.get( propertyName );
|
||||
}
|
||||
|
||||
if ( found == null ) {
|
||||
found = new CollectionPropertyPathSource( this, propertyName );
|
||||
collectionPropertyPathSourceMap.put( propertyName, found );
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
public static class EntityPropertyPathSource extends AbstractPropertyPathSource implements PropertyPathSource {
|
||||
private final XClass entityClass;
|
||||
|
||||
private EntityPropertyPathSource(XClass entityClass) {
|
||||
this.entityClass = entityClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String normalize(String path) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CompositePropertyPathSource extends AbstractPropertyPathSource implements PropertyPathSource {
|
||||
private final AbstractPropertyPathSource sourceOfComposite;
|
||||
private final String compositeName;
|
||||
|
||||
public CompositePropertyPathSource(AbstractPropertyPathSource sourceOfComposite, String compositeName) {
|
||||
this.sourceOfComposite = sourceOfComposite;
|
||||
this.compositeName = compositeName;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String normalize(String path) {
|
||||
return getSourceOfComposite().normalize( compositeName ) + "." + path;
|
||||
}
|
||||
|
||||
public AbstractPropertyPathSource getSourceOfComposite() {
|
||||
return sourceOfComposite;
|
||||
}
|
||||
|
||||
public String getCompositeName() {
|
||||
return compositeName;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CollectionPropertyPathSource extends AbstractPropertyPathSource implements PropertyPathSource {
|
||||
private final AbstractPropertyPathSource collectionSource;
|
||||
private final String collectionName;
|
||||
|
||||
private CollectionPropertyPathSource(AbstractPropertyPathSource collectionSource, String collectionName) {
|
||||
this.collectionSource = collectionSource;
|
||||
this.collectionName = collectionName;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String normalize(String path) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1367,7 +1367,7 @@ public abstract class CollectionBinder {
|
|||
column.setTable( collValue.getCollectionTable() );
|
||||
}
|
||||
elementBinder.setColumns( elementColumns );
|
||||
elementBinder.setType( property, elementClass, collValue.getOwnerEntityName() );
|
||||
elementBinder.setType( property, elementClass, collValue.getOwnerEntityName(), null );
|
||||
elementBinder.setPersistentClassName( propertyHolder.getEntityName() );
|
||||
elementBinder.setAccessType( accessType );
|
||||
collValue.setElement( elementBinder.make() );
|
||||
|
|
|
@ -278,7 +278,7 @@ public class MapBinder extends CollectionBinder {
|
|||
elementBinder.setExplicitType( mapKeyTypeAnnotation.value() );
|
||||
}
|
||||
else {
|
||||
elementBinder.setType( property, elementClass, this.collection.getOwnerEntityName() );
|
||||
elementBinder.setType( property, elementClass, this.collection.getOwnerEntityName(), null );
|
||||
}
|
||||
elementBinder.setPersistentClassName( propertyHolder.getEntityName() );
|
||||
elementBinder.setAccessType( accessType );
|
||||
|
|
|
@ -176,17 +176,23 @@ public class PropertyBinder {
|
|||
|
||||
private Property makePropertyAndValue() {
|
||||
validateBind();
|
||||
|
||||
LOG.debugf( "MetadataSourceProcessor property %s with lazy=%s", name, lazy );
|
||||
String containerClassName = holder == null ?
|
||||
null :
|
||||
holder.getClassName();
|
||||
final String containerClassName = holder.getClassName();
|
||||
holder.startingProperty( property );
|
||||
|
||||
simpleValueBinder = new SimpleValueBinder();
|
||||
simpleValueBinder.setMappings( mappings );
|
||||
simpleValueBinder.setPropertyName( name );
|
||||
simpleValueBinder.setReturnedClassName( returnedClassName );
|
||||
simpleValueBinder.setColumns( columns );
|
||||
simpleValueBinder.setPersistentClassName( containerClassName );
|
||||
simpleValueBinder.setType( property, returnedClass, containerClassName );
|
||||
simpleValueBinder.setType(
|
||||
property,
|
||||
returnedClass,
|
||||
containerClassName,
|
||||
holder.resolveAttributeConverterDefinition( property )
|
||||
);
|
||||
simpleValueBinder.setMappings( mappings );
|
||||
simpleValueBinder.setReferencedEntityName( referencedEntityName );
|
||||
simpleValueBinder.setAccessType( accessType );
|
||||
|
|
|
@ -23,15 +23,6 @@
|
|||
*/
|
||||
package org.hibernate.cfg.annotations;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.persistence.AttributeConverter;
|
||||
import javax.persistence.Convert;
|
||||
import javax.persistence.Converts;
|
||||
import javax.persistence.Enumerated;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Lob;
|
||||
|
@ -39,6 +30,12 @@ import javax.persistence.MapKeyEnumerated;
|
|||
import javax.persistence.MapKeyTemporal;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
import java.io.Serializable;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.AnnotationException;
|
||||
import org.hibernate.AssertionFailure;
|
||||
|
@ -46,9 +43,10 @@ import org.hibernate.MappingException;
|
|||
import org.hibernate.annotations.Nationalized;
|
||||
import org.hibernate.annotations.Parameter;
|
||||
import org.hibernate.annotations.Type;
|
||||
import org.hibernate.annotations.common.reflection.ClassLoadingException;
|
||||
import org.hibernate.annotations.common.reflection.XClass;
|
||||
import org.hibernate.annotations.common.reflection.XProperty;
|
||||
import org.hibernate.annotations.common.util.ReflectHelper;
|
||||
import org.hibernate.annotations.common.util.StandardClassLoaderDelegateImpl;
|
||||
import org.hibernate.cfg.AccessType;
|
||||
import org.hibernate.cfg.AttributeConverterDefinition;
|
||||
import org.hibernate.cfg.BinderHelper;
|
||||
|
@ -73,7 +71,6 @@ import org.hibernate.type.StandardBasicTypes;
|
|||
import org.hibernate.type.StringNVarcharType;
|
||||
import org.hibernate.type.WrappedMaterializedBlobType;
|
||||
import org.hibernate.usertype.DynamicParameterizedType;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
|
@ -144,7 +141,7 @@ public class SimpleValueBinder {
|
|||
|
||||
//TODO execute it lazily to be order safe
|
||||
|
||||
public void setType(XProperty property, XClass returnedClass, String declaringClassName) {
|
||||
public void setType(XProperty property, XClass returnedClass, String declaringClassName, AttributeConverterDefinition attributeConverterDefinition) {
|
||||
if ( returnedClass == null ) {
|
||||
// we cannot guess anything
|
||||
return;
|
||||
|
@ -302,11 +299,11 @@ public class SimpleValueBinder {
|
|||
defaultType = BinderHelper.isEmptyAnnotationValue( type ) ? returnedClassName : type;
|
||||
this.typeParameters = typeParameters;
|
||||
|
||||
applyAttributeConverter( property );
|
||||
applyAttributeConverter( property, attributeConverterDefinition );
|
||||
}
|
||||
|
||||
private void applyAttributeConverter(XProperty property) {
|
||||
if ( attributeConverterDefinition != null ) {
|
||||
private void applyAttributeConverter(XProperty property, AttributeConverterDefinition attributeConverterDefinition) {
|
||||
if ( attributeConverterDefinition == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -337,58 +334,7 @@ public class SimpleValueBinder {
|
|||
return;
|
||||
}
|
||||
|
||||
// @Convert annotations take precedence if present
|
||||
final Convert convertAnnotation = locateConvertAnnotation( property );
|
||||
if ( convertAnnotation != null ) {
|
||||
LOG.debugf(
|
||||
"Applying located @Convert AttributeConverter [%s] to attribute [%]",
|
||||
convertAnnotation.converter().getName(),
|
||||
property.getName()
|
||||
);
|
||||
attributeConverterDefinition = mappings.locateAttributeConverter( convertAnnotation.converter() );
|
||||
if ( attributeConverterDefinition == null ) {
|
||||
attributeConverterDefinition = makeAttributeConverterDefinition( convertAnnotation );
|
||||
}
|
||||
}
|
||||
else {
|
||||
attributeConverterDefinition = locateAutoApplyAttributeConverter( property );
|
||||
}
|
||||
}
|
||||
|
||||
private AttributeConverterDefinition makeAttributeConverterDefinition(Convert convertAnnotation) {
|
||||
try {
|
||||
return new AttributeConverterDefinition(
|
||||
(AttributeConverter) convertAnnotation.converter().newInstance(),
|
||||
false
|
||||
);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new AnnotationException( "Unable to create AttributeConverter instance", e );
|
||||
}
|
||||
}
|
||||
|
||||
private AttributeConverterDefinition locateAutoApplyAttributeConverter(XProperty property) {
|
||||
LOG.debugf(
|
||||
"Attempting to locate auto-apply AttributeConverter for property [%s:%s]",
|
||||
persistentClassName,
|
||||
property.getName()
|
||||
);
|
||||
final Class propertyType = mappings.getReflectionManager().toClass( property.getType() );
|
||||
for ( AttributeConverterDefinition attributeConverterDefinition : mappings.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;
|
||||
this.attributeConverterDefinition = attributeConverterDefinition;
|
||||
}
|
||||
|
||||
private boolean isAssociation() {
|
||||
|
@ -397,218 +343,6 @@ public class SimpleValueBinder {
|
|||
return referencedEntityName != null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Convert locateConvertAnnotation(XProperty property) {
|
||||
LOG.debugf(
|
||||
"Attempting to locate Convert annotation for property [%s:%s]",
|
||||
persistentClassName,
|
||||
property.getName()
|
||||
);
|
||||
|
||||
// first look locally on the property for @Convert/@Converts
|
||||
{
|
||||
Convert localConvertAnnotation = property.getAnnotation( Convert.class );
|
||||
if ( localConvertAnnotation != null ) {
|
||||
LOG.debugf(
|
||||
"Found matching local @Convert annotation [disableConversion=%s]",
|
||||
localConvertAnnotation.disableConversion()
|
||||
);
|
||||
return localConvertAnnotation.disableConversion()
|
||||
? null
|
||||
: localConvertAnnotation;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Converts localConvertsAnnotation = property.getAnnotation( Converts.class );
|
||||
if ( localConvertsAnnotation != null ) {
|
||||
for ( Convert localConvertAnnotation : localConvertsAnnotation.value() ) {
|
||||
if ( isLocalMatch( localConvertAnnotation, property ) ) {
|
||||
LOG.debugf(
|
||||
"Found matching @Convert annotation as part local @Converts [disableConversion=%s]",
|
||||
localConvertAnnotation.disableConversion()
|
||||
);
|
||||
return localConvertAnnotation.disableConversion()
|
||||
? null
|
||||
: localConvertAnnotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( persistentClassName == null ) {
|
||||
LOG.debug( "Persistent Class name not known during attempt to locate @Convert annotations" );
|
||||
return null;
|
||||
}
|
||||
|
||||
final XClass owner;
|
||||
try {
|
||||
final Class ownerClass = ReflectHelper.classForName( persistentClassName );
|
||||
owner = mappings.getReflectionManager().classForName( persistentClassName, ownerClass );
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new AnnotationException( "Unable to resolve Class reference during attempt to locate @Convert annotations" );
|
||||
}
|
||||
|
||||
return lookForEntityDefinedConvertAnnotation( property, owner );
|
||||
}
|
||||
|
||||
private Convert lookForEntityDefinedConvertAnnotation(XProperty property, XClass owner) {
|
||||
if ( owner == null ) {
|
||||
// we have hit the root of the entity hierarchy
|
||||
return null;
|
||||
}
|
||||
|
||||
LOG.debugf(
|
||||
"Attempting to locate any AttributeConverter defined via @Convert/@Converts on type-hierarchy [%s] to apply to attribute [%s]",
|
||||
owner.getName(),
|
||||
property.getName()
|
||||
);
|
||||
|
||||
{
|
||||
Convert convertAnnotation = owner.getAnnotation( Convert.class );
|
||||
if ( convertAnnotation != null && isMatch( convertAnnotation, property ) ) {
|
||||
LOG.debugf(
|
||||
"Found matching @Convert annotation [disableConversion=%s]",
|
||||
convertAnnotation.disableConversion()
|
||||
);
|
||||
return convertAnnotation.disableConversion() ? null : convertAnnotation;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Converts convertsAnnotation = owner.getAnnotation( Converts.class );
|
||||
if ( convertsAnnotation != null ) {
|
||||
for ( Convert convertAnnotation : convertsAnnotation.value() ) {
|
||||
if ( isMatch( convertAnnotation, property ) ) {
|
||||
LOG.debugf(
|
||||
"Found matching @Convert annotation as part @Converts [disableConversion=%s]",
|
||||
convertAnnotation.disableConversion()
|
||||
);
|
||||
return convertAnnotation.disableConversion() ? null : convertAnnotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// finally, look on superclass
|
||||
return lookForEntityDefinedConvertAnnotation( property, owner.getSuperclass() );
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean isLocalMatch(Convert convertAnnotation, XProperty property) {
|
||||
if ( StringHelper.isEmpty( convertAnnotation.attributeName() ) ) {
|
||||
return isTypeMatch( convertAnnotation.converter(), property );
|
||||
}
|
||||
|
||||
return property.getName().equals( convertAnnotation.attributeName() )
|
||||
&& isTypeMatch( convertAnnotation.converter(), property );
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean isMatch(Convert convertAnnotation, XProperty property) {
|
||||
return property.getName().equals( convertAnnotation.attributeName() );
|
||||
// return property.getName().equals( convertAnnotation.attributeName() )
|
||||
// && isTypeMatch( convertAnnotation.converter(), property );
|
||||
}
|
||||
|
||||
private boolean isTypeMatch(Class<? extends AttributeConverter> attributeConverterClass, XProperty property) {
|
||||
return areTypeMatch(
|
||||
extractEntityAttributeType( attributeConverterClass ),
|
||||
mappings.getReflectionManager().toClass( property.getType() )
|
||||
);
|
||||
}
|
||||
|
||||
private Class extractEntityAttributeType(Class<? extends AttributeConverter> attributeConverterClass) {
|
||||
// this is duplicated in SimpleValue...
|
||||
final TypeVariable[] attributeConverterTypeInformation = attributeConverterClass.getTypeParameters();
|
||||
if ( attributeConverterTypeInformation == null || attributeConverterTypeInformation.length < 2 ) {
|
||||
throw new AnnotationException(
|
||||
"AttributeConverter [" + attributeConverterClass.getName()
|
||||
+ "] did not retain parameterized type information"
|
||||
);
|
||||
}
|
||||
|
||||
if ( attributeConverterTypeInformation.length > 2 ) {
|
||||
LOG.debug(
|
||||
"AttributeConverter [" + attributeConverterClass.getName()
|
||||
+ "] specified more than 2 parameterized types"
|
||||
);
|
||||
}
|
||||
final Class entityAttributeJavaType = extractType( attributeConverterTypeInformation[0] );
|
||||
if ( entityAttributeJavaType == null ) {
|
||||
throw new AnnotationException(
|
||||
"Could not determine 'entity attribute' type from given AttributeConverter [" +
|
||||
attributeConverterClass.getName() + "]"
|
||||
);
|
||||
}
|
||||
return entityAttributeJavaType;
|
||||
}
|
||||
|
||||
private Class extractType(TypeVariable typeVariable) {
|
||||
java.lang.reflect.Type[] boundTypes = typeVariable.getBounds();
|
||||
if ( boundTypes == null || boundTypes.length != 1 ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (Class) boundTypes[0];
|
||||
}
|
||||
|
||||
private boolean areTypeMatch(Class converterDefinedType, Class propertyType) {
|
||||
if ( converterDefinedType == null ) {
|
||||
throw new AnnotationException( "AttributeConverter defined java type cannot be null" );
|
||||
}
|
||||
if ( propertyType == null ) {
|
||||
throw new AnnotationException( "Property defined java type cannot be null" );
|
||||
}
|
||||
|
||||
return converterDefinedType.equals( propertyType )
|
||||
|| arePrimitiveWrapperEquivalents( converterDefinedType, propertyType );
|
||||
}
|
||||
|
||||
private boolean arePrimitiveWrapperEquivalents(Class converterDefinedType, Class propertyType) {
|
||||
if ( converterDefinedType.isPrimitive() ) {
|
||||
return getWrapperEquivalent( converterDefinedType ).equals( propertyType );
|
||||
}
|
||||
else if ( propertyType.isPrimitive() ) {
|
||||
return getWrapperEquivalent( propertyType ).equals( converterDefinedType );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Class getWrapperEquivalent(Class primitive) {
|
||||
if ( ! primitive.isPrimitive() ) {
|
||||
throw new AssertionFailure( "Passed type for which to locate wrapper equivalent was not a primitive" );
|
||||
}
|
||||
|
||||
if ( boolean.class.equals( primitive ) ) {
|
||||
return Boolean.class;
|
||||
}
|
||||
else if ( char.class.equals( primitive ) ) {
|
||||
return Character.class;
|
||||
}
|
||||
else if ( byte.class.equals( primitive ) ) {
|
||||
return Byte.class;
|
||||
}
|
||||
else if ( short.class.equals( primitive ) ) {
|
||||
return Short.class;
|
||||
}
|
||||
else if ( int.class.equals( primitive ) ) {
|
||||
return Integer.class;
|
||||
}
|
||||
else if ( long.class.equals( primitive ) ) {
|
||||
return Long.class;
|
||||
}
|
||||
else if ( float.class.equals( primitive ) ) {
|
||||
return Float.class;
|
||||
}
|
||||
else if ( double.class.equals( primitive ) ) {
|
||||
return Double.class;
|
||||
}
|
||||
|
||||
throw new AssertionFailure( "Unexpected primitive type (VOID most likely) passed to getWrapperEquivalent" );
|
||||
}
|
||||
|
||||
private TemporalType getTemporalType(XProperty property) {
|
||||
if ( key ) {
|
||||
MapKeyTemporal ann = property.getAnnotation( MapKeyTemporal.class );
|
||||
|
@ -754,7 +488,7 @@ public class SimpleValueBinder {
|
|||
if ( simpleValue.getTypeName() != null && simpleValue.getTypeName().length() > 0
|
||||
&& simpleValue.getMappings().getTypeResolver().basic( simpleValue.getTypeName() ) == null ) {
|
||||
try {
|
||||
Class typeClass = ReflectHelper.classForName( simpleValue.getTypeName() );
|
||||
Class typeClass = StandardClassLoaderDelegateImpl.INSTANCE.classForName( simpleValue.getTypeName() );
|
||||
|
||||
if ( typeClass != null && DynamicParameterizedType.class.isAssignableFrom( typeClass ) ) {
|
||||
Properties parameters = simpleValue.getTypeParameters();
|
||||
|
@ -772,8 +506,8 @@ public class SimpleValueBinder {
|
|||
simpleValue.setTypeParameters( parameters );
|
||||
}
|
||||
}
|
||||
catch ( ClassNotFoundException cnfe ) {
|
||||
throw new MappingException( "Could not determine type for: " + simpleValue.getTypeName(), cnfe );
|
||||
catch (ClassLoadingException e) {
|
||||
throw new MappingException( "Could not determine type for: " + simpleValue.getTypeName(), e );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ public class SimpleValue implements KeyValue {
|
|||
private Properties typeParameters;
|
||||
private boolean cascadeDeleteEnabled;
|
||||
|
||||
private AttributeConverterDefinition jpaAttributeConverterDefinition;
|
||||
private AttributeConverterDefinition attributeConverterDefinition;
|
||||
private Type type;
|
||||
|
||||
public SimpleValue(Mappings mappings) {
|
||||
|
@ -354,7 +354,7 @@ public class SimpleValue implements KeyValue {
|
|||
return;
|
||||
}
|
||||
|
||||
if ( jpaAttributeConverterDefinition == null ) {
|
||||
if ( attributeConverterDefinition == 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...)
|
||||
|
@ -405,8 +405,8 @@ public class SimpleValue implements KeyValue {
|
|||
private Type buildAttributeConverterTypeAdapter() {
|
||||
// todo : validate the number of columns present here?
|
||||
|
||||
final Class entityAttributeJavaType = jpaAttributeConverterDefinition.getEntityAttributeType();
|
||||
final Class databaseColumnJavaType = jpaAttributeConverterDefinition.getDatabaseColumnType();
|
||||
final Class entityAttributeJavaType = attributeConverterDefinition.getEntityAttributeType();
|
||||
final Class databaseColumnJavaType = attributeConverterDefinition.getDatabaseColumnType();
|
||||
|
||||
|
||||
// resolve the JavaTypeDescriptor ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -430,7 +430,7 @@ 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(
|
||||
jpaAttributeConverterDefinition.getAttributeConverter(),
|
||||
attributeConverterDefinition.getAttributeConverter(),
|
||||
sqlTypeDescriptor,
|
||||
intermediateJavaTypeDescriptor
|
||||
);
|
||||
|
@ -444,7 +444,7 @@ public class SimpleValue implements KeyValue {
|
|||
);
|
||||
return new AttributeConverterTypeAdapter(
|
||||
name,
|
||||
jpaAttributeConverterDefinition.getAttributeConverter(),
|
||||
attributeConverterDefinition.getAttributeConverter(),
|
||||
sqlTypeDescriptorAdapter,
|
||||
entityAttributeJavaTypeDescriptor
|
||||
);
|
||||
|
@ -486,8 +486,8 @@ public class SimpleValue implements KeyValue {
|
|||
return getColumnInsertability();
|
||||
}
|
||||
|
||||
public void setJpaAttributeConverterDefinition(AttributeConverterDefinition jpaAttributeConverterDefinition) {
|
||||
this.jpaAttributeConverterDefinition = jpaAttributeConverterDefinition;
|
||||
public void setJpaAttributeConverterDefinition(AttributeConverterDefinition attributeConverterDefinition) {
|
||||
this.attributeConverterDefinition = attributeConverterDefinition;
|
||||
}
|
||||
|
||||
private void createParameterImpl() {
|
||||
|
|
|
@ -142,7 +142,6 @@ public class AttributeConverterTest extends BaseUnitTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@FailureExpected( jiraKey = "HHH-8449" )
|
||||
public void testBasicConverterDisableApplication() {
|
||||
Configuration cfg = new Configuration();
|
||||
cfg.addAttributeConverter( StringClobConverter.class, true );
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Inc.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.jpa.test.convert;
|
||||
|
||||
import javax.persistence.Convert;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.jpa.boot.spi.Bootstrap;
|
||||
import org.hibernate.jpa.test.PersistenceUnitDescriptorAdapter;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.type.CompositeType;
|
||||
import org.hibernate.type.StringType;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||
|
||||
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
|
||||
|
||||
/**
|
||||
* Tests MappedSuperclass/Entity overriding of Convert definitions
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SimpleEmbeddableOverriddenConverterTest extends BaseUnitTestCase {
|
||||
/**
|
||||
* Test outcome of annotations exclusively.
|
||||
*/
|
||||
@Test
|
||||
public void testSimpleConvertOverrides() {
|
||||
final PersistenceUnitDescriptorAdapter pu = new PersistenceUnitDescriptorAdapter() {
|
||||
@Override
|
||||
public List<String> getManagedClassNames() {
|
||||
return Arrays.asList( Person.class.getName() );
|
||||
}
|
||||
};
|
||||
|
||||
final Map settings = new HashMap();
|
||||
// settings.put( AvailableSettings.HBM2DDL_AUTO, "create-drop" );
|
||||
|
||||
EntityManagerFactory emf = Bootstrap.getEntityManagerFactoryBuilder( pu, settings ).build();
|
||||
|
||||
final SessionFactoryImplementor sfi = emf.unwrap( SessionFactoryImplementor.class );
|
||||
try {
|
||||
final EntityPersister ep = sfi.getEntityPersister( Person.class.getName() );
|
||||
|
||||
CompositeType homeAddressType = assertTyping( CompositeType.class, ep.getPropertyType( "homeAddress" ) );
|
||||
Type homeAddressCityType = findCompositeAttributeType( homeAddressType, "city" );
|
||||
assertTyping( StringType.class, homeAddressCityType );
|
||||
}
|
||||
finally {
|
||||
emf.close();
|
||||
}
|
||||
}
|
||||
|
||||
public Type findCompositeAttributeType(CompositeType compositeType, String attributeName) {
|
||||
int pos = 0;
|
||||
for ( String name : compositeType.getPropertyNames() ) {
|
||||
if ( name.equals( attributeName ) ) {
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
|
||||
if ( pos >= compositeType.getPropertyNames().length ) {
|
||||
throw new IllegalStateException( "Could not locate attribute index for [" + attributeName + "] in composite" );
|
||||
}
|
||||
|
||||
return compositeType.getSubtypes()[pos];
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class Address {
|
||||
public String street;
|
||||
@Convert(converter = SillyStringConverter.class)
|
||||
public String city;
|
||||
}
|
||||
|
||||
@Entity( name="Person" )
|
||||
public static class Person {
|
||||
@Id
|
||||
public Integer id;
|
||||
@Embedded
|
||||
@Convert( attributeName = "city", disableConversion = true )
|
||||
public Address homeAddress;
|
||||
}
|
||||
}
|
|
@ -43,7 +43,6 @@ import org.hibernate.type.Type;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.testing.FailureExpected;
|
||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||
|
||||
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
|
||||
|
@ -58,7 +57,6 @@ public class SimpleOverriddenConverterTest extends BaseUnitTestCase {
|
|||
* Test outcome of annotations exclusively.
|
||||
*/
|
||||
@Test
|
||||
@FailureExpected( jiraKey = "HHH-8449" )
|
||||
public void testSimpleConvertOverrides() {
|
||||
final PersistenceUnitDescriptorAdapter pu = new PersistenceUnitDescriptorAdapter() {
|
||||
@Override
|
||||
|
|
|
@ -43,7 +43,7 @@ ext {
|
|||
|
||||
// Annotations
|
||||
commons_annotations:
|
||||
'org.hibernate.common:hibernate-commons-annotations:4.0.2.Final@jar',
|
||||
'org.hibernate.common:hibernate-commons-annotations:4.0.3.Final@jar',
|
||||
jandex: 'org.jboss:jandex:1.1.0.Alpha1',
|
||||
classmate: 'com.fasterxml:classmate:0.8.0',
|
||||
|
||||
|
|
Loading…
Reference in New Issue