diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/ComponentAttributeSourceImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/ComponentAttributeSourceImpl.java index af0944b1d2..443649bc31 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/ComponentAttributeSourceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/ComponentAttributeSourceImpl.java @@ -25,11 +25,13 @@ package org.hibernate.metamodel.source.annotations.entity; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import org.jboss.jandex.AnnotationInstance; +import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.Value; import org.hibernate.mapping.PropertyGeneration; import org.hibernate.metamodel.source.LocalBindingContext; @@ -53,21 +55,22 @@ import org.hibernate.metamodel.source.binder.SingularAttributeNature; * @author Hardy Ferentschik */ public class ComponentAttributeSourceImpl implements ComponentAttributeSource { + private static final String PATH_SEPERATOR = "."; private final EmbeddableClass embeddableClass; private final Value> classReference; private final Map attributeOverrides; private final String path; - public ComponentAttributeSourceImpl(EmbeddableClass embeddableClass, Map attributeOverrides) { + public ComponentAttributeSourceImpl(EmbeddableClass embeddableClass, String parentPath, Map attributeOverrides) { this.embeddableClass = embeddableClass; this.classReference = new Value>( embeddableClass.getClass() ); this.attributeOverrides = attributeOverrides; - String tmpPath = embeddableClass.getEmbeddedAttributeName(); - ConfiguredClass parent = embeddableClass.getParent(); - while ( parent != null && parent instanceof EmbeddableClass ) { - tmpPath = ( (EmbeddableClass) parent ).getEmbeddedAttributeName() + "." + tmpPath; + if ( StringHelper.isEmpty( parentPath ) ) { + path = embeddableClass.getEmbeddedAttributeName(); + } + else { + path = parentPath + "." + embeddableClass.getEmbeddedAttributeName(); } - path = tmpPath; } @Override @@ -125,17 +128,18 @@ public class ComponentAttributeSourceImpl implements ComponentAttributeSource { List attributeList = new ArrayList(); for ( BasicAttribute attribute : embeddableClass.getSimpleAttributes() ) { AttributeOverride attributeOverride = null; - String tmp = getPath() + "." + attribute.getName(); + String tmp = getPath() + PATH_SEPERATOR + attribute.getName(); if ( attributeOverrides.containsKey( tmp ) ) { attributeOverride = attributeOverrides.get( tmp ); } attributeList.add( new SingularAttributeSourceImpl( attribute, attributeOverride ) ); } - for ( EmbeddableClass component : embeddableClass.getEmbeddedClasses().values() ) { + for ( EmbeddableClass embeddable : embeddableClass.getEmbeddedClasses().values() ) { attributeList.add( new ComponentAttributeSourceImpl( - component, - embeddableClass.getAttributeOverrideMap() + embeddable, + getPath(), + createAggregatedOverrideMap() ) ); } @@ -191,13 +195,11 @@ public class ComponentAttributeSourceImpl implements ComponentAttributeSource { @Override public boolean isLazy() { - // todo : implement return false; } @Override public boolean isIncludedInOptimisticLocking() { - // todo : implement return true; } @@ -215,4 +217,29 @@ public class ComponentAttributeSourceImpl implements ComponentAttributeSource { public boolean areValuesNullableByDefault() { return true; } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append( "ComponentAttributeSourceImpl" ); + sb.append( "{embeddableClass=" ).append( embeddableClass.getConfiguredClass().getSimpleName() ); + sb.append( '}' ); + return sb.toString(); + } + + private Map createAggregatedOverrideMap() { + // add all overrides passed down to this instance - they override overrides ;-) which are defined further down + // the embeddable chain + Map aggregatedOverrideMap = new HashMap( + attributeOverrides + ); + + for ( Map.Entry entry : embeddableClass.getAttributeOverrideMap().entrySet() ) { + String fullPath = getPath() + PATH_SEPERATOR + entry.getKey(); + if ( !aggregatedOverrideMap.containsKey( fullPath ) ) { + aggregatedOverrideMap.put( fullPath, entry.getValue() ); + } + } + return aggregatedOverrideMap; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/ConfiguredClass.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/ConfiguredClass.java index bb707ec377..58d7d81416 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/ConfiguredClass.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/ConfiguredClass.java @@ -608,7 +608,7 @@ public class ConfiguredClass { JPADotNames.ATTRIBUTE_OVERRIDE ); if ( attributeOverrideAnnotation != null ) { - String prefix = createPathPrefix( attributeOverrideAnnotation ); + String prefix = createPathPrefix( attributeOverrideAnnotation.target() ); AttributeOverride override = new AttributeOverride( prefix, attributeOverrideAnnotation ); attributeOverrideList.put( override.getAttributePath(), override ); } @@ -620,7 +620,7 @@ public class ConfiguredClass { if ( attributeOverridesAnnotation != null ) { AnnotationInstance[] annotationInstances = attributeOverridesAnnotation.value().asNestedArray(); for ( AnnotationInstance annotationInstance : annotationInstances ) { - String prefix = createPathPrefix( annotationInstance ); + String prefix = createPathPrefix( attributeOverridesAnnotation.target() ); AttributeOverride override = new AttributeOverride( prefix, annotationInstance ); attributeOverrideList.put( override.getAttributePath(), override ); } @@ -628,9 +628,8 @@ public class ConfiguredClass { return attributeOverrideList; } - private String createPathPrefix(AnnotationInstance attributeOverrideAnnotation) { + private String createPathPrefix(AnnotationTarget target) { String prefix = null; - AnnotationTarget target = attributeOverrideAnnotation.target(); if ( target instanceof FieldInfo || target instanceof MethodInfo ) { prefix = JandexHelper.getPropertyName( target ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/EntitySourceImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/EntitySourceImpl.java index f9a9f0939c..baa2b64468 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/EntitySourceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/EntitySourceImpl.java @@ -176,7 +176,7 @@ public class EntitySourceImpl implements EntitySource { attributeList.add( new SingularAttributeSourceImpl( attribute ) ); } for ( EmbeddableClass component : entityClass.getEmbeddedClasses().values() ) { - attributeList.add( new ComponentAttributeSourceImpl( component, entityClass.getAttributeOverrideMap() ) ); + attributeList.add( new ComponentAttributeSourceImpl( component, "", entityClass.getAttributeOverrideMap() ) ); } for ( AssociationAttribute associationAttribute : entityClass.getAssociationAttributes() ) { attributeList.add( new ToOneAttributeSourceImpl( associationAttribute ) ); diff --git a/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/entity/EmbeddableBindingTest.java b/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/entity/EmbeddableBindingTest.java index 1dc7899403..d90ad4d0fb 100644 --- a/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/entity/EmbeddableBindingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/entity/EmbeddableBindingTest.java @@ -24,6 +24,7 @@ package org.hibernate.metamodel.source.annotations.entity; import javax.persistence.AttributeOverride; +import javax.persistence.AttributeOverrides; import javax.persistence.Column; import javax.persistence.Embeddable; import javax.persistence.Embedded; @@ -52,22 +53,22 @@ public class EmbeddableBindingTest extends BaseAnnotationBindingTestCase { private int id; @Embedded - private Address address; + private Phone phone; } @Embeddable - class Address { - String street; - String city; - String postCode; + class Phone { + String countryCode; + String areaCode; + String number; } @Test - @Resources(annotatedClasses = { User.class, Address.class }) + @Resources(annotatedClasses = { User.class, Phone.class }) public void testEmbeddable() { EntityBinding binding = getEntityBinding( User.class ); - final String componentName = "address"; + final String componentName = "phone"; assertNotNull( binding.locateAttributeBinding( componentName ) ); assertTrue( binding.locateAttributeBinding( componentName ) instanceof ComponentAttributeBinding ); ComponentAttributeBinding componentBinding = (ComponentAttributeBinding) binding.locateAttributeBinding( @@ -77,13 +78,13 @@ public class EmbeddableBindingTest extends BaseAnnotationBindingTestCase { // todo - is this really correct? Does the path start w/ the class name assertEquals( "Wrong path", - "org.hibernate.metamodel.source.annotations.entity.EmbeddableBindingTest$User.address", + "org.hibernate.metamodel.source.annotations.entity.EmbeddableBindingTest$User.phone", componentBinding.getPathBase() ); - assertNotNull( componentBinding.locateAttributeBinding( "street" ) ); - assertNotNull( componentBinding.locateAttributeBinding( "city" ) ); - assertNotNull( componentBinding.locateAttributeBinding( "postCode" ) ); + assertNotNull( componentBinding.locateAttributeBinding( "countryCode" ) ); + assertNotNull( componentBinding.locateAttributeBinding( "areaCode" ) ); + assertNotNull( componentBinding.locateAttributeBinding( "number" ) ); } @Entity @@ -119,6 +120,141 @@ public class EmbeddableBindingTest extends BaseAnnotationBindingTestCase { assertEquals( "Attribute override specifies a custom column name", "FUBAR", column.getColumnName().getName() ); assertEquals( "Attribute override specifies a custom size", 42, column.getSize().getLength() ); } + + @Embeddable + public class Address { + protected String street; + protected String city; + protected String state; + @Embedded + protected Zipcode zipcode; + } + + @Embeddable + public class Zipcode { + protected String zip; + protected String plusFour; + } + + @Entity + public class Customer { + @Id + protected Integer id; + protected String name; + @AttributeOverrides( { + @AttributeOverride(name = "state", + column = @Column(name = "ADDR_STATE")), + @AttributeOverride(name = "zipcode.zip", + column = @Column(name = "ADDR_ZIP")) + }) + @Embedded + protected Address address; + } + + @Test + @Resources(annotatedClasses = { Zipcode.class, Address.class, Customer.class }) + public void testNestedEmbeddable() { + EntityBinding binding = getEntityBinding( Customer.class ); + + final String addressComponentName = "address"; + assertNotNull( binding.locateAttributeBinding( addressComponentName ) ); + assertTrue( binding.locateAttributeBinding( addressComponentName ) instanceof ComponentAttributeBinding ); + ComponentAttributeBinding attributeComponentBinding = (ComponentAttributeBinding) binding.locateAttributeBinding( + addressComponentName + ); + + assertNotNull( attributeComponentBinding.locateAttributeBinding( "street" ) ); + assertNotNull( attributeComponentBinding.locateAttributeBinding( "city" ) ); + assertNotNull( attributeComponentBinding.locateAttributeBinding( "state" ) ); + + BasicAttributeBinding stateAttribute = (BasicAttributeBinding) attributeComponentBinding.locateAttributeBinding( + "state" + ); + org.hibernate.metamodel.relational.Column column = (org.hibernate.metamodel.relational.Column) stateAttribute.getValue(); + assertEquals( + "Attribute override specifies a custom column name", + "ADDR_STATE", + column.getColumnName().getName() + ); + + + final String zipComponentName = "zipcode"; + assertNotNull( attributeComponentBinding.locateAttributeBinding( zipComponentName ) ); + assertTrue( attributeComponentBinding.locateAttributeBinding( zipComponentName ) instanceof ComponentAttributeBinding ); + ComponentAttributeBinding zipComponentBinding = (ComponentAttributeBinding) attributeComponentBinding.locateAttributeBinding( + zipComponentName + ); + + BasicAttributeBinding nameAttribute = (BasicAttributeBinding) zipComponentBinding.locateAttributeBinding( "zip" ); + column = (org.hibernate.metamodel.relational.Column) nameAttribute.getValue(); + assertEquals( + "Attribute override specifies a custom column name", + "ADDR_ZIP", + column.getColumnName().getName() + ); + } + + @Embeddable + public class A { + @Embedded + @AttributeOverrides( { + @AttributeOverride(name = "foo", column = @Column(name = "BAR")), + @AttributeOverride(name = "fubar", column = @Column(name = "A_WINS")) + }) + protected B b; + } + + @Embeddable + public class B { + protected String foo; + private String fubar; + } + + @Entity + public class C { + @Id + int id; + + @Embedded + @AttributeOverride(name = "b.fubar", column = @Column(name = "C_WINS")) + protected A a; + } + + @Test + @Resources(annotatedClasses = { A.class, B.class, C.class }) + public void testAttributeOverrideInEmbeddable() { + EntityBinding binding = getEntityBinding( C.class ); + + final String aComponentName = "a"; + assertNotNull( binding.locateAttributeBinding( aComponentName ) ); + assertTrue( binding.locateAttributeBinding( aComponentName ) instanceof ComponentAttributeBinding ); + ComponentAttributeBinding aComponentBinding = (ComponentAttributeBinding) binding.locateAttributeBinding( + aComponentName + ); + + final String bComponentName = "b"; + assertNotNull( aComponentBinding.locateAttributeBinding( bComponentName ) ); + assertTrue( aComponentBinding.locateAttributeBinding( bComponentName ) instanceof ComponentAttributeBinding ); + ComponentAttributeBinding bComponentBinding = (ComponentAttributeBinding) aComponentBinding.locateAttributeBinding( + bComponentName + ); + + BasicAttributeBinding attribute = (BasicAttributeBinding) bComponentBinding.locateAttributeBinding( "foo" ); + org.hibernate.metamodel.relational.Column column = (org.hibernate.metamodel.relational.Column) attribute.getValue(); + assertEquals( + "Attribute override specifies a custom column name", + "BAR", + column.getColumnName().getName() + ); + + attribute = (BasicAttributeBinding) bComponentBinding.locateAttributeBinding( "fubar" ); + column = (org.hibernate.metamodel.relational.Column) attribute.getValue(); + assertEquals( + "Attribute override specifies a custom column name", + "C_WINS", + column.getColumnName().getName() + ); + } }