HHH-8469 - Application of JPA 2.1 AttributeConverters

This commit is contained in:
Steve Ebersole 2013-09-03 11:42:54 -05:00
parent 21c3e2bd80
commit 151dd1531b
9 changed files with 473 additions and 1 deletions

View File

@ -24,11 +24,19 @@
package org.hibernate.cfg; package org.hibernate.cfg;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.persistence.Convert;
import javax.persistence.Converts;
import javax.persistence.JoinTable; import javax.persistence.JoinTable;
import org.jboss.logging.Logger;
import org.hibernate.AnnotationException;
import org.hibernate.annotations.common.AssertionFailure; import org.hibernate.annotations.common.AssertionFailure;
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.util.ReflectHelper;
import org.hibernate.cfg.annotations.EntityBinder; import org.hibernate.cfg.annotations.EntityBinder;
import org.hibernate.internal.CoreLogging;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.mapping.Join; import org.hibernate.mapping.Join;
import org.hibernate.mapping.KeyValue; import org.hibernate.mapping.KeyValue;
@ -41,6 +49,8 @@ import org.hibernate.mapping.Table;
* @author Emmanuel Bernard * @author Emmanuel Bernard
*/ */
public class ClassPropertyHolder extends AbstractPropertyHolder { public class ClassPropertyHolder extends AbstractPropertyHolder {
private static final Logger log = CoreLogging.logger( ClassPropertyHolder.class );
private PersistentClass persistentClass; private PersistentClass persistentClass;
private Map<String, Join> joins; private Map<String, Join> joins;
private transient Map<String, Join> joinsPerRealTableName; private transient Map<String, Join> joinsPerRealTableName;
@ -202,4 +212,78 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
public boolean isOrWithinEmbeddedId() { public boolean isOrWithinEmbeddedId() {
return false; return false;
} }
// @Override
// public AttributeConverterDefinition resolveAttributeConverter(String attributeName) {
//
// // @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 = getMappings().locateAttributeConverter( convertAnnotation.converter() );
// }
// else {
// attributeConverterDefinition = locateAutoApplyAttributeConverter( property );
// }
// }
//
// @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 );
// }
} }

View File

@ -67,6 +67,8 @@ public interface PropertyHolder {
String getPath(); String getPath();
// public AttributeConverterDefinition resolveAttributeConverter(String attributeName);
/** /**
* return null if the column is not overridden, or an array of column if true * return null if the column is not overridden, or an array of column if true
*/ */

View File

@ -346,12 +346,27 @@ public class SimpleValueBinder {
property.getName() property.getName()
); );
attributeConverterDefinition = mappings.locateAttributeConverter( convertAnnotation.converter() ); attributeConverterDefinition = mappings.locateAttributeConverter( convertAnnotation.converter() );
if ( attributeConverterDefinition == null ) {
attributeConverterDefinition = makeAttributeConverterDefinition( convertAnnotation );
}
} }
else { else {
attributeConverterDefinition = locateAutoApplyAttributeConverter( property ); 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) { private AttributeConverterDefinition locateAutoApplyAttributeConverter(XProperty property) {
LOG.debugf( LOG.debugf(
"Attempting to locate auto-apply AttributeConverter for property [%s:%s]", "Attempting to locate auto-apply AttributeConverter for property [%s:%s]",

View File

@ -49,6 +49,7 @@ import javax.persistence.CascadeType;
import javax.persistence.CollectionTable; import javax.persistence.CollectionTable;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.ColumnResult; import javax.persistence.ColumnResult;
import javax.persistence.Convert;
import javax.persistence.DiscriminatorColumn; import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType; import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue; import javax.persistence.DiscriminatorValue;
@ -247,6 +248,7 @@ public class JPAOverriddenAnnotationReader implements AnnotationReader {
annotationToXml.put( Cacheable.class, "cacheable" ); annotationToXml.put( Cacheable.class, "cacheable" );
annotationToXml.put( Index.class, "index" ); annotationToXml.put( Index.class, "index" );
annotationToXml.put( ForeignKey.class, "foreign-key" ); annotationToXml.put( ForeignKey.class, "foreign-key" );
annotationToXml.put( Convert.class, "convert" );
} }
private XMLContext xmlContext; private XMLContext xmlContext;
@ -341,7 +343,7 @@ public class JPAOverriddenAnnotationReader implements AnnotationReader {
/* /*
* The idea is to create annotation proxies for the xml configuration elements. Using this proxy annotations together * The idea is to create annotation proxies for the xml configuration elements. Using this proxy annotations together
* with the {@code JPAMetadataprovider} allows to handle xml configuration the same way as annotation configuration. * with the {@link JPAMetadataProvider} allows to handle xml configuration the same way as annotation configuration.
*/ */
private void initAnnotations() { private void initAnnotations() {
if ( annotations == null ) { if ( annotations == null ) {

View File

@ -0,0 +1,43 @@
/*
* 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.AttributeConverter;
import javax.persistence.Converter;
/**
* @author Steve Ebersole
*/
@Converter
public class SillyStringConverter implements AttributeConverter<String,String> {
@Override
public String convertToDatabaseColumn(String attribute) {
return attribute;
}
@Override
public String convertToEntityAttribute(String dbData) {
return dbData;
}
}

View File

@ -0,0 +1,101 @@
/*
* 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.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.StringType;
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;
/**
* Tests MappedSuperclass/Entity overriding of Convert definitions
*
* @author Steve Ebersole
*/
public class SimpleOverriddenConverterTest extends BaseUnitTestCase {
/**
* Test outcome of annotations exclusively.
*/
@Test
@FailureExpected( jiraKey = "HHH-8449" )
public void testSimpleConvertOverrides() {
final PersistenceUnitDescriptorAdapter pu = new PersistenceUnitDescriptorAdapter() {
@Override
public List<String> getManagedClassNames() {
return Arrays.asList( Super.class.getName(), Sub.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( Sub.class.getName() );
Type type = ep.getPropertyType( "it" );
assertTyping( StringType.class, type );
}
finally {
emf.close();
}
}
@MappedSuperclass
public static class Super {
@Id
public Integer id;
@Convert(converter = SillyStringConverter.class)
public String it;
}
@Entity(name = "Sub")
@Convert( attributeName = "it", disableConversion = true )
public static class Sub extends Super {
}
}

View File

@ -0,0 +1,174 @@
/*
* 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.Entity;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import java.util.Arrays;
import java.util.Collections;
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.StringType;
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;
import static org.junit.Assert.fail;
/**
* Test simple application of Convert annotation via XML.
*
* @author Steve Ebersole
*/
public class SimpleXmlOverriddenTest extends BaseUnitTestCase {
@Test
public void baseline() {
final PersistenceUnitDescriptorAdapter pu = new PersistenceUnitDescriptorAdapter() {
@Override
public List<String> getManagedClassNames() {
return Arrays.asList( Super.class.getName(), Sub.class.getName() );
}
// No mapping file should mean that the converter is applied
};
final Map settings = Collections.emptyMap();
EntityManagerFactory emf = Bootstrap.getEntityManagerFactoryBuilder( pu, settings ).build();
final SessionFactoryImplementor sfi = emf.unwrap( SessionFactoryImplementor.class );
try {
final EntityPersister ep = sfi.getEntityPersister( Sub.class.getName() );
Type type = ep.getPropertyType( "it" );
try {
assertTyping( StringType.class, type );
fail( "Expected AttributeConverter to be applied" );
}
catch (AssertionError expected) {
}
}
finally {
emf.close();
}
}
/**
* Test outcome of applying overrides via orm.xml, specifically at the entity level
*/
@Test
public void testDefinitionAtEntityLevel() {
final PersistenceUnitDescriptorAdapter pu = new PersistenceUnitDescriptorAdapter() {
@Override
public List<String> getManagedClassNames() {
return Arrays.asList( Super.class.getName(), Sub.class.getName() );
}
@Override
public List<String> getMappingFileNames() {
return Arrays.asList( "org/hibernate/jpa/test/convert/simple-override.xml" );
}
};
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( Sub.class.getName() );
Type type = ep.getPropertyType( "it" );
assertTyping( StringType.class, type );
}
finally {
emf.close();
}
}
/**
* Test outcome of applying overrides via orm.xml, specifically at the entity level
*/
@Test
public void testDefinitionAtAttributeLevel() {
final PersistenceUnitDescriptorAdapter pu = new PersistenceUnitDescriptorAdapter() {
@Override
public List<String> getManagedClassNames() {
return Arrays.asList( Super.class.getName(), Sub.class.getName() );
}
@Override
public List<String> getMappingFileNames() {
return Arrays.asList( "org/hibernate/jpa/test/convert/simple-override2.xml" );
}
};
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( Sub.class.getName() );
Type type = ep.getPropertyType( "it" );
assertTyping( StringType.class, type );
}
finally {
emf.close();
}
}
@MappedSuperclass
public static class Super {
@Id
public Integer id;
@Convert(converter = SillyStringConverter.class)
public String it;
}
@Entity(name = "Sub")
// the xml disabled conversion on the Sub#it attribute
// Essentially the same test as org.hibernate.jpa.test.convert.SimpleOverriddenConverterTest, but through XML
//@Convert( attributeName = "it", disableConversion = true )
public static class Sub extends Super {
}
}

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm http://xmlns.jcp.org/xml/ns/persistence/orm/orm_2_1.xsd"
version="2.1">
<package>org.hibernate.jpa.test.convert</package>
<entity class="org.hibernate.jpa.test.convert.SimpleXmlOverriddenTest$Sub">
<convert attribute-name="it" disable-conversion="true" />
</entity>
</entity-mappings>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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
-->
<entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm http://xmlns.jcp.org/xml/ns/persistence/orm/orm_2_1.xsd"
version="2.1">
<package>org.hibernate.jpa.test.convert</package>
<entity class="org.hibernate.jpa.test.convert.SimpleXmlOverriddenTest$Sub">
<!--
<attributes>
<basic name="it">
<convert disable-conversion="true" />
</basic>
</attributes>
-->
</entity>
</entity-mappings>