HHH-6392 Making sure that overrides get applied and adding more tests
This commit is contained in:
parent
593066b7f3
commit
959056dee7
|
@ -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 = ".";
|
||||
|
|
|
@ -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}.
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue