HHH-6392 Making sure that overrides get applied and adding more tests

This commit is contained in:
Hardy Ferentschik 2011-07-12 14:57:14 +02:00
parent 593066b7f3
commit 959056dee7
6 changed files with 130 additions and 74 deletions

View File

@ -12,6 +12,7 @@ import org.hibernate.metamodel.source.annotations.util.JandexHelper;
* are creating during annotation processing and then applied onto the persistence attributes.
*
* @author Hardy Ferentschik
* @todo Take care of prefixes of the form 'element', 'key' and 'value'. Add another type enum to handle this. (HF)
*/
public class AttributeOverride {
private static final String PROPERTY_PATH_SEPARATOR = ".";

View File

@ -80,10 +80,6 @@ public abstract class MappedAttribute implements Comparable<MappedAttribute> {
return typeParameters;
}
public Map<DotName, List<AnnotationInstance>> getAnnotations() {
return annotations;
}
/**
* Returns the annotation with the specified name or {@code null}
*
@ -119,6 +115,10 @@ public abstract class MappedAttribute implements Comparable<MappedAttribute> {
return sb.toString();
}
Map<DotName, List<AnnotationInstance>> annotations() {
return annotations;
}
/**
* We need to check whether the is an explicit type specified via {@link org.hibernate.annotations.Type}.
*

View File

@ -91,7 +91,7 @@ public class SimpleAttribute extends MappedAttribute {
}
public static SimpleAttribute createSimpleAttribute(SimpleAttribute simpleAttribute, ColumnValues columnValues) {
SimpleAttribute attribute = new SimpleAttribute( simpleAttribute.getName(), simpleAttribute.getType(), simpleAttribute.getAnnotations(), false );
SimpleAttribute attribute = new SimpleAttribute( simpleAttribute.getName(), simpleAttribute.getType(), simpleAttribute.annotations(), false );
attribute.columnValues = columnValues;
return attribute;
}

View File

@ -47,10 +47,12 @@ import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.FieldInfo;
import org.jboss.jandex.MethodInfo;
import org.jboss.logging.Logger;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.metamodel.source.annotations.AnnotationBindingContext;
import org.hibernate.metamodel.source.annotations.JPADotNames;
import org.hibernate.metamodel.source.annotations.attribute.AssociationAttribute;
@ -69,6 +71,11 @@ import org.hibernate.metamodel.source.annotations.util.ReflectionHelper;
*/
public class ConfiguredClass {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(
CoreMessageLogger.class,
ConfiguredClass.class.getName()
);
/**
* The parent of this configured class or {@code null} in case this configured class is the root of a hierarchy.
*/
@ -191,8 +198,8 @@ public class ConfiguredClass {
return attribute;
}
public AttributeOverride geAttributeOverrideForPath(String propertyPath) {
return attributeOverrideMap.get( propertyPath );
public Map<String, AttributeOverride> getAttributeOverrideMap() {
return attributeOverrideMap;
}
@Override
@ -320,33 +327,10 @@ public class ConfiguredClass {
AccessType accessType = JandexHelper.getValueAsEnum( accessAnnotation, "value", AccessType.class );
// when class access type is field
// overriding access annotations must be placed on properties and have the access type PROPERTY
if ( AccessType.FIELD.equals( classAccessType ) ) {
if ( !( annotationTarget instanceof MethodInfo ) ) {
// todo log warning !?
continue;
}
if ( !AccessType.PROPERTY.equals( accessType ) ) {
// todo log warning !?
continue;
}
if ( !isExplicitAttributeAccessAnnotationPlacedCorrectly( annotationTarget, accessType ) ) {
continue;
}
// when class access type is property
// overriding access annotations must be placed on fields and have the access type FIELD
if ( AccessType.PROPERTY.equals( classAccessType ) ) {
if ( !( annotationTarget instanceof FieldInfo ) ) {
// todo log warning !?
continue;
}
if ( !AccessType.FIELD.equals( accessType ) ) {
// todo log warning !?
continue;
}
}
// the placement is correct, get the member
Member member;
@ -386,6 +370,53 @@ public class ConfiguredClass {
return explicitAccessMembers;
}
private boolean isExplicitAttributeAccessAnnotationPlacedCorrectly(AnnotationTarget annotationTarget, AccessType accessType) {
// when the access type of the class is FIELD
// overriding access annotations must be placed on properties AND have the access type PROPERTY
if ( AccessType.FIELD.equals( classAccessType ) ) {
if ( !( annotationTarget instanceof MethodInfo ) ) {
LOG.tracef(
"The access type of class %s is AccessType.FIELD. To override the access for an attribute " +
"@Access has to be placed on the property (getter)", classInfo.name().toString()
);
return false;
}
if ( !AccessType.PROPERTY.equals( accessType ) ) {
LOG.tracef(
"The access type of class %s is AccessType.FIELD. To override the access for an attribute " +
"@Access has to be placed on the property (getter) with an access type of AccessType.PROPERTY. " +
"Using AccessType.FIELD on the property has no effect",
classInfo.name().toString()
);
return false;
}
}
// when the access type of the class is PROPERTY
// overriding access annotations must be placed on fields and have the access type FIELD
if ( AccessType.PROPERTY.equals( classAccessType ) ) {
if ( !( annotationTarget instanceof FieldInfo ) ) {
LOG.tracef(
"The access type of class %s is AccessType.PROPERTY. To override the access for a field " +
"@Access has to be placed on the field ", classInfo.name().toString()
);
return false;
}
if ( !AccessType.FIELD.equals( accessType ) ) {
LOG.tracef(
"The access type of class %s is AccessType.PROPERTY. To override the access for a field " +
"@Access has to be placed on the field with an access type of AccessType.FIELD. " +
"Using AccessType.PROPERTY on the field has no effect",
classInfo.name().toString()
);
return false;
}
}
return true;
}
private void createMappedProperty(Member member, ResolvedTypeWithMembers resolvedType) {
final String attributeName = ReflectionHelper.getPropertyName( member );
ResolvedMember[] resolvedMembers;
@ -519,8 +550,13 @@ public class ConfiguredClass {
return resolvedMember.getType().getErasedType();
}
}
// todo - what to do here
return null;
throw new AssertionFailure(
String.format(
"Unable to resolve type of attribute %s of class %s",
name,
classInfo.name().toString()
)
);
}
/**

View File

@ -613,33 +613,55 @@ public class EntityBinder {
}
private void bindAttributes(EntityBinding entityBinding) {
// collect attribute overrides as we map the attributes
Map<String, AttributeOverride> attributeOverrideMap = new HashMap<String, AttributeOverride>();
// bind the attributes of this entity
AttributeContainer entity = entityBinding.getEntity();
bindAttributes( entityBinding, entity, entityClass, null );
bindAttributes( entityBinding, entity, entityClass, attributeOverrideMap );
// bind potential mapped super class attributes
ConfiguredClass childClass = entityClass;
attributeOverrideMap.putAll( entityClass.getAttributeOverrideMap() );
ConfiguredClass parent = entityClass.getParent();
Hierarchical superTypeContainer = entityBinding.getEntity().getSuperType();
while ( containsPotentialMappedSuperclassAttributes( parent ) ) {
bindAttributes( entityBinding, superTypeContainer, parent, childClass );
childClass = parent;
while ( containsMappedSuperclassAttributes( parent ) ) {
bindAttributes( entityBinding, superTypeContainer, parent, attributeOverrideMap );
addNewOverridesToMap( parent, attributeOverrideMap );
parent = parent.getParent();
superTypeContainer = superTypeContainer.getSuperType();
}
}
private boolean containsPotentialMappedSuperclassAttributes(ConfiguredClass parent) {
private void addNewOverridesToMap(ConfiguredClass parent, Map<String, AttributeOverride> attributeOverrideMap) {
Map<String, AttributeOverride> overrides = parent.getAttributeOverrideMap();
for ( Map.Entry<String, AttributeOverride> entry : overrides.entrySet() ) {
if ( !attributeOverrideMap.containsKey( entry.getKey() ) ) {
attributeOverrideMap.put( entry.getKey(), entry.getValue() );
}
}
}
private boolean containsMappedSuperclassAttributes(ConfiguredClass parent) {
return parent != null && ( ConfiguredClassType.MAPPED_SUPERCLASS.equals( parent.getConfiguredClassType() ) ||
ConfiguredClassType.NON_ENTITY.equals( parent.getConfiguredClassType() ) );
}
private void bindAttributes(EntityBinding entityBinding, AttributeContainer attributeContainer, ConfiguredClass configuredClass, ConfiguredClass childClass) {
/**
* Creates attribute bindings for the attributes of {@code configuredClass}
*
* @param entityBinding The entity binding for the class we are currently binding
* @param attributeContainer The domain attribute container to which to add the attribute (could be the entity itself, or a mapped super class
* or a component)
* @param configuredClass the configured containing the attributes to be bound
* @param attributeOverrideMap a map with the accumulated attribute overrides
*/
private void bindAttributes(EntityBinding entityBinding, AttributeContainer attributeContainer, ConfiguredClass configuredClass, Map<String, AttributeOverride> attributeOverrideMap) {
for ( SimpleAttribute simpleAttribute : configuredClass.getSimpleAttributes() ) {
String attributeName = simpleAttribute.getName();
if(childClass != null && childClass.geAttributeOverrideForPath(attributeName) != null) {
AttributeOverride override = childClass.geAttributeOverrideForPath(attributeName);
// if there is a override apply it
AttributeOverride override = attributeOverrideMap.get( attributeName );
if ( override != null ) {
simpleAttribute = SimpleAttribute.createSimpleAttribute( simpleAttribute, override.getColumnValues() );
}
@ -665,7 +687,7 @@ public class EntityBinder {
// bind potential mapped super class embeddables
ConfiguredClass parent = entityClass.getParent();
Hierarchical superTypeContainer = entityBinding.getEntity().getSuperType();
while ( containsPotentialMappedSuperclassAttributes( parent ) ) {
while ( containsMappedSuperclassAttributes( parent ) ) {
bindEmbeddedAttributes( entityBinding, superTypeContainer, parent );
parent = parent.getParent();
superTypeContainer = superTypeContainer.getSuperType();

View File

@ -25,8 +25,6 @@ package org.hibernate.metamodel.source.annotations.entity;
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
@ -36,11 +34,9 @@ import org.junit.Test;
import org.hibernate.metamodel.binding.AttributeBinding;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.metamodel.domain.NonEntity;
import org.hibernate.metamodel.domain.Superclass;
import org.hibernate.metamodel.relational.Column;
import org.hibernate.metamodel.relational.SimpleValue;
import org.hibernate.metamodel.relational.Tuple;
import org.hibernate.testing.FailureExpected;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
@ -54,34 +50,37 @@ import static junit.framework.Assert.assertTrue;
*/
public class MappedSuperclassTests extends BaseAnnotationBindingTestCase {
@Test
public void testMappedSuperclass() {
buildMetadataSources( MyMappedSuperClass.class, MyEntity.class, Address.class );
public void testSimpleAttributeOverrideInMappedSuperclass() {
buildMetadataSources( MyMappedSuperClass.class, MyEntity.class, MyMappedSuperClassBase.class );
EntityBinding binding = getEntityBinding( MyEntity.class );
assertEquals( "Wrong entity name", MyEntity.class.getName(), binding.getEntity().getName() );
assertEquals(
"Wrong entity name",
MyMappedSuperClass.class.getName(),
binding.getEntity().getSuperType().getName()
);
assertTrue( binding.getEntity().getSuperType() instanceof Superclass );
AttributeBinding nameBinding = binding.getAttributeBinding( "name" );
assertNotNull( "the name attribute should be bound to the subclass", nameBinding );
assertNotNull( "the name attribute should be bound to MyEntity", nameBinding );
assertTrue( "The binding should be a simple column", nameBinding.getValue() instanceof Tuple );
Tuple tuple = (Tuple) nameBinding.getValue();
SimpleValue value = tuple.values().iterator().next();
assertTrue( value instanceof Column );
Column column = (Column) value;
assertEquals( "Wrong column name", "`MY_NAME`", column.getColumnName().toString() );
AttributeBinding idBinding = binding.getEntityIdentifier().getValueBinding();
assertNotNull( "the id attribute should be bound", idBinding );
}
@Test
public void testNoEntity() {
public void testLastAttributeOverrideWins() {
buildMetadataSources( MyMappedSuperClass.class, MyEntity.class, MyMappedSuperClassBase.class );
EntityBinding binding = getEntityBinding( MyEntity.class );
AttributeBinding fooBinding = binding.getAttributeBinding( "foo" );
assertNotNull( "the foo attribute should be bound to MyEntity", fooBinding );
Tuple tuple = (Tuple) fooBinding.getValue();
SimpleValue value = tuple.values().iterator().next();
assertTrue( value instanceof Column );
Column column = (Column) value;
assertEquals( "Wrong column name", "`MY_FOO`", column.getColumnName().toString() );
}
@Test
public void testNonEntityBaseClass() {
buildMetadataSources( SubclassOfNoEntity.class, NoEntity.class );
EntityBinding binding = getEntityBinding( SubclassOfNoEntity.class );
assertEquals( "Wrong entity name", SubclassOfNoEntity.class.getName(), binding.getEntity().getName() );
@ -90,28 +89,26 @@ public class MappedSuperclassTests extends BaseAnnotationBindingTestCase {
}
@MappedSuperclass
class MyMappedSuperClass {
class MyMappedSuperClassBase {
@Id
private int id;
String foo;
}
@MappedSuperclass
@AttributeOverride(name = "foo", column = @javax.persistence.Column(name = "SUPER_FOO"))
class MyMappedSuperClass extends MyMappedSuperClassBase {
String name;
}
@Entity
@AttributeOverrides( {
@AttributeOverride(name = "name", column = @javax.persistence.Column(name = "MY_NAME")),
@AttributeOverride(name = "address.street", column = @javax.persistence.Column(name = "MY_STREET"))
@AttributeOverride(name = "foo", column = @javax.persistence.Column(name = "MY_FOO"))
})
class MyEntity extends MyMappedSuperClass {
private Long count;
@AttributeOverride(name = "city", column = @javax.persistence.Column(name = "MY_CITY"))
@Embedded
Address address;
}
@Embeddable
class Address {
String street;
String city;
}
class NoEntity {