diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/AnnotationBinder.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/AnnotationBinder.java index ab7a2b55a7..e1831773ac 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/AnnotationBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/AnnotationBinder.java @@ -36,8 +36,13 @@ import org.jboss.logging.Logger; import org.hibernate.HibernateException; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.metamodel.MetadataSources; +import org.hibernate.metamodel.binding.EntityBinding; +import org.hibernate.metamodel.domain.Hierarchical; +import org.hibernate.metamodel.domain.NonEntity; +import org.hibernate.metamodel.domain.Superclass; import org.hibernate.metamodel.source.annotation.xml.XMLEntityMappings; import org.hibernate.metamodel.source.annotations.entity.ConfiguredClassHierarchy; +import org.hibernate.metamodel.source.annotations.entity.ConfiguredClassType; import org.hibernate.metamodel.source.annotations.entity.EntityBinder; import org.hibernate.metamodel.source.annotations.entity.EntityClass; import org.hibernate.metamodel.source.annotations.global.FetchProfileBinder; @@ -56,10 +61,11 @@ import org.hibernate.service.classloading.spi.ClassLoaderService; /** * Main class responsible to creating and binding the Hibernate meta-model from annotations. - * This binder only has to deal with annotation index. XML configuration is already processed and pseudo annotations - * are added to the annotation index. + * This binder only has to deal with the (jandex) annotation index/repository. XML configuration is already processed + * and pseudo annotations are created. * * @author Hardy Ferentschik + * @see org.hibernate.metamodel.source.annotations.xml.OrmXmlParser */ public class AnnotationBinder implements Binder { private static final CoreMessageLogger LOG = Logger.getMessageLogger( @@ -105,6 +111,12 @@ public class AnnotationBinder implements Binder { } } + /** + * Adds the class w/ the specified name to the jandex index. + * + * @param indexer The jandex indexer + * @param className the fully qualified class name to be indexed + */ private void indexClass(Indexer indexer, String className) { InputStream stream = classLoaderService().locateResourceStream( className ); try { @@ -134,16 +146,33 @@ public class AnnotationBinder implements Binder { @Override public void bindMappingMetadata(MetadataSources sources, List processedEntityNames) { - // need to order our annotated entities into an order we can process AnnotationBindingContext context = new AnnotationBindingContext( index, metadata.getServiceRegistry() ); - Set> hierarchies = ConfiguredClassHierarchyBuilder.createEntityHierarchies( context ); + // need to order our annotated entities into an order we can process + Set> hierarchies = ConfiguredClassHierarchyBuilder.createEntityHierarchies( + context + ); // now we process each hierarchy one at the time + Hierarchical parent = null; for ( ConfiguredClassHierarchy hierarchy : hierarchies ) { for ( EntityClass entityClass : hierarchy ) { - LOG.bindingEntityFromAnnotatedClass( entityClass.getName() ); - EntityBinder entityBinder = new EntityBinder( metadata, entityClass ); - entityBinder.bind(); + // for classes annotated w/ @Entity we create a EntityBinding + if ( ConfiguredClassType.ENTITY.equals( entityClass.getConfiguredClassType() ) ) { + LOG.bindingEntityFromAnnotatedClass( entityClass.getName() ); + EntityBinder entityBinder = new EntityBinder( metadata, entityClass, parent ); + EntityBinding binding = entityBinder.bind(); + parent = binding.getEntity(); + } + // for classes annotated w/ @MappedSuperclass we just create the domain instance + // the attribute bindings will be part of the first entity subclass + else if ( ConfiguredClassType.MAPPED_SUPERCLASS.equals( entityClass.getConfiguredClassType() ) ) { + parent = new Superclass( entityClass.getName(), parent ); + } + // for classes which are not annotated at all we create the NonEntity domain class + // todo - not sure whether this is needed. It might be that we don't need this information (HF) + else { + parent = new NonEntity( entityClass.getName(), parent ); + } } } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/EntityBinder.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/EntityBinder.java index 18ceca2e63..a92845c8d5 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/EntityBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/EntityBinder.java @@ -24,6 +24,7 @@ package org.hibernate.metamodel.source.annotations.entity; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import javax.persistence.GenerationType; @@ -65,7 +66,6 @@ import org.hibernate.metamodel.relational.UniqueKey; import org.hibernate.metamodel.source.annotations.HibernateDotNames; import org.hibernate.metamodel.source.annotations.JPADotNames; import org.hibernate.metamodel.source.annotations.attribute.AssociationAttribute; -import org.hibernate.metamodel.source.annotations.attribute.DiscriminatorColumnValues; import org.hibernate.metamodel.source.annotations.attribute.MappedAttribute; import org.hibernate.metamodel.source.annotations.attribute.SimpleAttribute; import org.hibernate.metamodel.source.annotations.attribute.state.binding.AttributeBindingStateImpl; @@ -88,22 +88,20 @@ import org.hibernate.persister.entity.EntityPersister; public class EntityBinder { private final EntityClass entityClass; private final MetadataImplementor meta; + private final Hierarchical superType; - private Schema.Name schemaName; - - public EntityBinder(MetadataImplementor metadata, EntityClass entityClass) { + public EntityBinder(MetadataImplementor metadata, EntityClass entityClass, Hierarchical superType) { this.entityClass = entityClass; this.meta = metadata; + this.superType = superType; } - public void bind() { + public EntityBinding bind() { EntityBinding entityBinding = new EntityBinding(); - EntityBindingStateImpl entityBindingState = new EntityBindingStateImpl( getSuperType(), entityClass ); + EntityBindingStateImpl entityBindingState = new EntityBindingStateImpl( superType, entityClass ); bindJpaEntityAnnotation( entityBindingState ); bindHibernateEntityAnnotation( entityBindingState ); // optional hibernate specific @org.hibernate.annotations.Entity - - schemaName = createSchemaName(); bindTable( entityBinding ); // bind entity level annotations @@ -115,18 +113,16 @@ public class EntityBinder { bindCustomSQL( entityBindingState ); bindRowId( entityBindingState ); bindBatchSize( entityBindingState ); - entityBinding.initialize( meta, entityBindingState ); bindInheritance( entityBinding ); - - // bind all attributes - simple as well as associations bindAttributes( entityBinding ); bindEmbeddedAttributes( entityBinding ); - // take care of the id, attributes and relations - if ( entityClass.isRoot() ) { + + // take care of the id, attributes and relations + if ( entityClass.isEntityRoot() ) { bindId( entityBinding ); } @@ -134,6 +130,7 @@ public class EntityBinder { // last, but not least we initialize and register the new EntityBinding meta.addEntity( entityBinding ); + return entityBinding; } private void bindTableUniqueConstraints(EntityBinding entityBinding) { @@ -199,12 +196,7 @@ public class EntityBinder { entityClass.getClassInfo() ); SimpleAttribute discriminatorAttribute = SimpleAttribute.createDiscriminatorAttribute( typeAnnotations ); - bindSingleMappedAttribute( entityBinding, entityBinding.getEntity(), discriminatorAttribute ); - - if ( !( discriminatorAttribute.getColumnValues() instanceof DiscriminatorColumnValues ) ) { - throw new AssertionFailure( "Expected discriminator column values" ); - } } private void bindWhereFilter(EntityBindingStateImpl entityBindingState) { @@ -426,7 +418,7 @@ public class EntityBinder { } private void bindTable(EntityBinding entityBinding) { - final Schema schema = meta.getDatabase().getSchema( schemaName ); + final Schema schema = meta.getDatabase().getSchema( createSchemaName() ); final Identifier tableName = Identifier.toIdentifier( entityClass.getPrimaryTableName() ); org.hibernate.metamodel.relational.Table table = schema.getTable( tableName ); if ( table == null ) { @@ -476,40 +468,9 @@ public class EntityBinder { entityBindingState.setJpaEntityName( name ); } - private void bindEmbeddedIdAnnotation(EntityBinding entityBinding) { - AnnotationInstance idAnnotation = JandexHelper.getSingleAnnotation( - entityClass.getClassInfo(), JPADotNames.EMBEDDED_ID - ); - - String idName = JandexHelper.getPropertyName( idAnnotation.target() ); - MappedAttribute idAttribute = entityClass.getMappedAttribute( idName ); - if ( !( idAttribute instanceof SimpleAttribute ) ) { - throw new AssertionFailure( "Unexpected attribute type for id attribute" ); - } - - SingularAttribute attribute = entityBinding.getEntity().getOrCreateComponentAttribute( idName ); - - - SimpleAttributeBinding attributeBinding = entityBinding.makeSimpleIdAttributeBinding( attribute ); - - attributeBinding.initialize( new AttributeBindingStateImpl( (SimpleAttribute) idAttribute ) ); - - TupleRelationalStateImpl state = new TupleRelationalStateImpl(); - EmbeddableClass embeddableClass = entityClass.getEmbeddedClasses().get( idName ); - for ( MappedAttribute attr : embeddableClass.getMappedAttributes() ) { - state.addValueState( new ColumnRelationalStateImpl( (SimpleAttribute) attr, meta ) ); - } - attributeBinding.initialize( state ); - Map parms = new HashMap( 1 ); - parms.put( IdentifierGenerator.ENTITY_NAME, entityBinding.getEntity().getName() ); - IdGenerator generator = new IdGenerator( "NAME","assigned", parms); - entityBinding.getEntityIdentifier().setIdGenerator( generator ); - // entityBinding.getEntityIdentifier().createIdentifierGenerator( meta.getIdentifierGeneratorFactory() ); - } - - private void bindSingleIdAnnotation(EntityBinding entityBinding) { + private void bindEmbeddedIdAnnotation(EntityBinding entityBinding) { AnnotationInstance idAnnotation = JandexHelper.getSingleAnnotation( - entityClass.getClassInfo(), JPADotNames.ID + entityClass.getClassInfo(), JPADotNames.EMBEDDED_ID ); String idName = JandexHelper.getPropertyName( idAnnotation.target() ); @@ -518,13 +479,59 @@ public class EntityBinder { throw new AssertionFailure( "Unexpected attribute type for id attribute" ); } - Attribute attribute = entityBinding.getEntity().getOrCreateSingularAttribute( idName ); + SingularAttribute attribute = entityBinding.getEntity().getOrCreateComponentAttribute( idName ); + + + SimpleAttributeBinding attributeBinding = entityBinding.makeSimpleIdAttributeBinding( attribute ); + + attributeBinding.initialize( new AttributeBindingStateImpl( (SimpleAttribute) idAttribute ) ); + + TupleRelationalStateImpl state = new TupleRelationalStateImpl(); + EmbeddableClass embeddableClass = entityClass.getEmbeddedClasses().get( idName ); + for ( SimpleAttribute attr : embeddableClass.getSimpleAttributes() ) { + state.addValueState( new ColumnRelationalStateImpl( attr, meta ) ); + } + attributeBinding.initialize( state ); + Map parms = new HashMap( 1 ); + parms.put( IdentifierGenerator.ENTITY_NAME, entityBinding.getEntity().getName() ); + IdGenerator generator = new IdGenerator( "NAME", "assigned", parms ); + entityBinding.getEntityIdentifier().setIdGenerator( generator ); + // entityBinding.getEntityIdentifier().createIdentifierGenerator( meta.getIdentifierGeneratorFactory() ); + } + + private void bindSingleIdAnnotation(EntityBinding entityBinding) { + // we know we are dealing w/ a single @Id, but potentially it is defined in a mapped super class + ConfiguredClass configuredClass = entityClass; + EntityClass superEntity = entityClass.getEntityParent(); + Hierarchical container = entityBinding.getEntity(); + Iterator iter = null; + while ( configuredClass != null && configuredClass != superEntity ) { + iter = configuredClass.getIdAttributes().iterator(); + if ( iter.hasNext() ) { + break; + } + configuredClass = configuredClass.getParent(); + container = container.getSuperType(); + } + + // if we could not find the attribute our assumptions were wrong + if ( iter == null || !iter.hasNext() ) { + throw new AnnotationException( + String.format( + "Unable to find id attribute for class %s", + entityClass.getName() + ) + ); + } + + // now that we have the id attribute we can create the attribute and binding + MappedAttribute idAttribute = iter.next(); + Attribute attribute = container.getOrCreateSingularAttribute( idAttribute.getName() ); SimpleAttributeBinding attributeBinding = entityBinding.makeSimpleIdAttributeBinding( attribute ); attributeBinding.initialize( new AttributeBindingStateImpl( (SimpleAttribute) idAttribute ) ); attributeBinding.initialize( new ColumnRelationalStateImpl( (SimpleAttribute) idAttribute, meta ) ); - bindSingleIdGeneratedValue( entityBinding, idName ); - + bindSingleIdGeneratedValue( entityBinding, idAttribute.getName() ); } private void bindSingleIdGeneratedValue(EntityBinding entityBinding, String idPropertyName) { @@ -552,7 +559,7 @@ public class EntityBinder { if ( idGenerator == null ) { throw new MappingException( String.format( - "@GeneratedValue on %s.%s refering an undefined generator [%s]", + "@GeneratedValue on %s.%s referring an undefined generator [%s]", entityClass.getName(), idName, generator @@ -580,7 +587,7 @@ public class EntityBinder { ) ); } - if( idGenerator == null ) { + if ( idGenerator == null ) { idGenerator = new IdGenerator( "NAME", strategy, new HashMap() ); entityBinding.getEntityIdentifier().setIdGenerator( idGenerator ); } @@ -588,44 +595,74 @@ public class EntityBinder { } private void bindAttributes(EntityBinding entityBinding) { - for ( MappedAttribute mappedAttribute : entityClass.getMappedAttributes() ) { - if ( mappedAttribute instanceof AssociationAttribute ) { - bindAssociationAttribute( - entityBinding, - entityBinding.getEntity(), - (AssociationAttribute) mappedAttribute - ); - } - else { - bindSingleMappedAttribute( - entityBinding, - entityBinding.getEntity(), - (SimpleAttribute) mappedAttribute - ); - } + // bind the attributes of this entity + AttributeContainer entity = entityBinding.getEntity(); + bindAttributes( entityBinding, entity, entityClass ); + + // bind potential mapped super class attributes + ConfiguredClass parent = entityClass.getParent(); + Hierarchical superTypeContainer = entityBinding.getEntity().getSuperType(); + while ( containsPotentialMappedSuperclassAttributes( parent ) ) { + bindAttributes( entityBinding, superTypeContainer, parent ); + parent = parent.getParent(); + superTypeContainer = superTypeContainer.getSuperType(); + } + } + + private boolean containsPotentialMappedSuperclassAttributes(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) { + for ( SimpleAttribute simpleAttribute : configuredClass.getSimpleAttributes() ) { + bindSingleMappedAttribute( + entityBinding, + attributeContainer, + simpleAttribute + ); + } + for ( AssociationAttribute associationAttribute : configuredClass.getAssociationAttributes() ) { + bindAssociationAttribute( + entityBinding, + attributeContainer, + associationAttribute + ); } } private void bindEmbeddedAttributes(EntityBinding entityBinding) { - for ( Map.Entry entry : entityClass.getEmbeddedClasses().entrySet() ) { + AttributeContainer entity = entityBinding.getEntity(); + bindEmbeddedAttributes( entityBinding, entity, entityClass ); + + // bind potential mapped super class embeddables + ConfiguredClass parent = entityClass.getParent(); + Hierarchical superTypeContainer = entityBinding.getEntity().getSuperType(); + while ( containsPotentialMappedSuperclassAttributes( parent ) ) { + bindEmbeddedAttributes( entityBinding, superTypeContainer, parent ); + parent = parent.getParent(); + superTypeContainer = superTypeContainer.getSuperType(); + } + } + + private void bindEmbeddedAttributes(EntityBinding entityBinding, AttributeContainer attributeContainer, ConfiguredClass configuredClass) { + for ( Map.Entry entry : configuredClass.getEmbeddedClasses().entrySet() ) { String attributeName = entry.getKey(); EmbeddableClass embeddedClass = entry.getValue(); - SingularAttribute component = entityBinding.getEntity().getOrCreateComponentAttribute( attributeName ); - for ( MappedAttribute mappedAttribute : embeddedClass.getMappedAttributes() ) { - if ( mappedAttribute instanceof AssociationAttribute ) { - bindAssociationAttribute( - entityBinding, - component.getAttributeContainer(), - (AssociationAttribute) mappedAttribute - ); - } - else { - bindSingleMappedAttribute( - entityBinding, - component.getAttributeContainer(), - (SimpleAttribute) mappedAttribute - ); - } + SingularAttribute component = attributeContainer.getOrCreateComponentAttribute( attributeName ); + for ( SimpleAttribute simpleAttribute : embeddedClass.getSimpleAttributes() ) { + bindSingleMappedAttribute( + entityBinding, + component.getAttributeContainer(), + simpleAttribute + ); + } + for ( AssociationAttribute associationAttribute : embeddedClass.getAssociationAttributes() ) { + bindAssociationAttribute( + entityBinding, + component.getAttributeContainer(), + associationAttribute + ); } } } @@ -754,21 +791,5 @@ public class EntityBinder { entityBindingState.setExplicitPolymorphism( PolymorphismType.EXPLICIT.equals( polymorphism ) ); entityBindingState.setOptimisticLock( optimisticLock ); } - - private Hierarchical getSuperType() { - ConfiguredClass parent = entityClass.getParent(); - if ( parent == null ) { - return null; - } - - EntityBinding parentBinding = meta.getEntityBinding( parent.getName() ); - if ( parentBinding == null ) { - throw new AssertionFailure( - "Parent entity " + parent.getName() + " of entity " + entityClass.getName() + " not yet created!" - ); - } - - return parentBinding.getEntity(); - } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/EntityClass.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/EntityClass.java index c400aa0670..d14b96afe0 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/EntityClass.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/EntityClass.java @@ -23,12 +23,14 @@ */ package org.hibernate.metamodel.source.annotations.entity; +import java.util.ArrayList; import java.util.List; import javax.persistence.AccessType; import org.jboss.jandex.AnnotationInstance; import org.jboss.jandex.AnnotationValue; import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.DotName; import org.hibernate.AnnotationException; import org.hibernate.MappingException; @@ -38,18 +40,17 @@ import org.hibernate.metamodel.source.annotations.JPADotNames; import org.hibernate.metamodel.source.annotations.util.JandexHelper; /** - * Represents an entity, mapped superclass or component configured via annotations/xml. + * Represents an entity or mapped superclass configured via annotations/xml. * * @author Hardy Ferentschik */ public class EntityClass extends ConfiguredClass { private final AccessType hierarchyAccessType; - private final InheritanceType inheritanceType; private final boolean hasOwnTable; private final String primaryTableName; - private final IdType idType; + private final EntityClass jpaEntityParent; public EntityClass(ClassInfo classInfo, EntityClass parent, @@ -61,11 +62,27 @@ public class EntityClass extends ConfiguredClass { this.hierarchyAccessType = hierarchyAccessType; this.inheritanceType = inheritanceType; this.idType = determineIdType(); - + this.jpaEntityParent = findJpaEntitySuperClass(); this.hasOwnTable = definesItsOwnTable(); this.primaryTableName = determinePrimaryTableName(); } + /** + * @return Returns the next JPA super entity for this entity class or {@code null} in case there is none. + */ + public EntityClass getEntityParent() { + return jpaEntityParent; + } + + /** + * @return Returns {@code true} is this entity class is the root of the class hierarchy in the JPA sense, which + * means there are no more super classes which are annotated with @Entity. There can, however, be mapped superclasses + * or non entities in the actual java type hierarchy. + */ + public boolean isEntityRoot() { + return jpaEntityParent == null; + } + public InheritanceType getInheritanceType() { return inheritanceType; } @@ -86,7 +103,7 @@ public class EntityClass extends ConfiguredClass { public String toString() { final StringBuilder sb = new StringBuilder(); sb.append( "EntityClass" ); - sb.append( "{name=" ).append( getName() ); + sb.append( "{name=" ).append( getConfiguredClass().getSimpleName() ); sb.append( ", hierarchyAccessType=" ).append( hierarchyAccessType ); sb.append( ", inheritanceType=" ).append( inheritanceType ); sb.append( ", hasOwnTable=" ).append( hasOwnTable ); @@ -97,18 +114,33 @@ public class EntityClass extends ConfiguredClass { } private boolean definesItsOwnTable() { - // mapped super classes and embeddables don't have their own tables - if ( ConfiguredClassType.MAPPED_SUPERCLASS.equals( getConfiguredClassType() ) || ConfiguredClassType.EMBEDDABLE - .equals( getConfiguredClassType() ) ) { + // mapped super classes don't have their own tables + if ( ConfiguredClassType.MAPPED_SUPERCLASS.equals( getConfiguredClassType() ) ) { return false; } if ( InheritanceType.SINGLE_TABLE.equals( inheritanceType ) ) { - return isRoot(); + if ( isEntityRoot() ) { + return true; + } + else { + return false; + } } return true; } + private EntityClass findJpaEntitySuperClass() { + ConfiguredClass tmpConfiguredClass = this.getParent(); + while ( tmpConfiguredClass != null ) { + if ( ConfiguredClassType.ENTITY.equals( tmpConfiguredClass.getConfiguredClassType() ) ) { + return (EntityClass) tmpConfiguredClass; + } + tmpConfiguredClass = tmpConfiguredClass.getParent(); + } + return null; + } + private String determinePrimaryTableName() { String tableName = null; if ( hasOwnTable() ) { @@ -125,26 +157,23 @@ public class EntityClass extends ConfiguredClass { } } else if ( getParent() != null - && !getParent().getConfiguredClassType().equals( ConfiguredClassType.MAPPED_SUPERCLASS ) - && !getParent().getConfiguredClassType().equals( ConfiguredClassType.EMBEDDABLE ) ) { + && !getParent().getConfiguredClassType().equals( ConfiguredClassType.MAPPED_SUPERCLASS ) ) { tableName = ( (EntityClass) getParent() ).getPrimaryTableName(); } return tableName; } private IdType determineIdType() { - List idAnnotations = getClassInfo().annotations().get( JPADotNames.ID ); - List embeddedIdAnnotations = getClassInfo() - .annotations() - .get( JPADotNames.EMBEDDED_ID ); + List idAnnotations = findIdAnnotations( JPADotNames.ID ); + List embeddedIdAnnotations = findIdAnnotations( JPADotNames.EMBEDDED_ID ); - if ( idAnnotations != null && embeddedIdAnnotations != null ) { + if ( !idAnnotations.isEmpty() && !embeddedIdAnnotations.isEmpty() ) { throw new MappingException( "@EmbeddedId and @Id cannot be used together. Check the configuration for " + getName() + "." ); } - if ( embeddedIdAnnotations != null ) { + if ( !embeddedIdAnnotations.isEmpty() ) { if ( embeddedIdAnnotations.size() == 1 ) { return IdType.EMBEDDED; } @@ -153,7 +182,7 @@ public class EntityClass extends ConfiguredClass { } } - if ( idAnnotations != null ) { + if ( !idAnnotations.isEmpty() ) { if ( idAnnotations.size() == 1 ) { return IdType.SIMPLE; } @@ -163,4 +192,21 @@ public class EntityClass extends ConfiguredClass { } return IdType.NONE; } + + private List findIdAnnotations(DotName idAnnotationType) { + List idAnnotationList = new ArrayList(); + if ( getClassInfo().annotations().get( idAnnotationType ) != null ) { + idAnnotationList.addAll( getClassInfo().annotations().get( idAnnotationType ) ); + } + ConfiguredClass parent = getParent(); + while ( parent != null && ( ConfiguredClassType.MAPPED_SUPERCLASS.equals( parent.getConfiguredClassType() ) || + ConfiguredClassType.NON_ENTITY.equals( parent.getConfiguredClassType() ) ) ) { + if ( parent.getClassInfo().annotations().get( idAnnotationType ) != null ) { + idAnnotationList.addAll( parent.getClassInfo().annotations().get( idAnnotationType ) ); + } + parent = parent.getParent(); + + } + return idAnnotationList; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/state/binding/EntityBindingStateImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/state/binding/EntityBindingStateImpl.java index 1d9d5285f9..1868a02f0e 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/state/binding/EntityBindingStateImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/state/binding/EntityBindingStateImpl.java @@ -78,11 +78,11 @@ public class EntityBindingStateImpl implements EntityBindingState { private Set synchronizedTableNames; - public EntityBindingStateImpl(Hierarchical superType, EntityClass configuredClass) { - this.className = configuredClass.getName(); + public EntityBindingStateImpl(Hierarchical superType, EntityClass entityClass) { + this.className = entityClass.getName(); this.superType = superType; - this.isRoot = configuredClass.isRoot(); - this.inheritanceType = configuredClass.getInheritanceType(); + this.isRoot = entityClass.isEntityRoot(); + this.inheritanceType = entityClass.getInheritanceType(); this.synchronizedTableNames = new HashSet(); this.batchSize = -1; } diff --git a/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/entity/MappedSuperclassWithAttributeOverrideTests.java b/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/entity/MappedSuperclassWithAttributeOverrideTests.java new file mode 100644 index 0000000000..53d50ce6a2 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/entity/MappedSuperclassWithAttributeOverrideTests.java @@ -0,0 +1,101 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, 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.metamodel.source.annotations.entity; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.MappedSuperclass; + +import org.junit.Test; + +import org.hibernate.action.internal.EntityIdentityInsertAction; +import org.hibernate.metamodel.binding.AttributeBinding; +import org.hibernate.metamodel.binding.EntityBinding; +import org.hibernate.metamodel.binding.EntityIdentifier; +import org.hibernate.metamodel.domain.NonEntity; +import org.hibernate.metamodel.domain.Superclass; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; + +/** + * Tests for {@code j.p.AttributeOverrides} and {@code j.p.AttributeOverride}. + * + * @author Hardy Ferentschik + */ +public class MappedSuperclassWithAttributeOverrideTests extends BaseAnnotationBindingTestCase { + @Test + public void testMappedSuperclass() { + buildMetadataSources( MyMappedSuperClass.class, MyEntity.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 ); + + AttributeBinding idBinding = binding.getEntityIdentifier().getValueBinding(); + assertNotNull( "the id attribute should be bound", idBinding ); + } + + @Test + public void testNoEntity() { + buildMetadataSources( SubclassOfNoEntity.class, NoEntity.class ); + EntityBinding binding = getEntityBinding( SubclassOfNoEntity.class ); + assertEquals( "Wrong entity name", SubclassOfNoEntity.class.getName(), binding.getEntity().getName() ); + assertEquals( "Wrong entity name", NoEntity.class.getName(), binding.getEntity().getSuperType().getName() ); + assertTrue( binding.getEntity().getSuperType() instanceof NonEntity ); + } + + @MappedSuperclass + class MyMappedSuperClass { + @Id + private int id; + String name; + int age; + } + + @Entity + class MyEntity extends MyMappedSuperClass { + private Long count; + } + + class NoEntity { + String name; + int age; + } + + @Entity + class SubclassOfNoEntity extends NoEntity { + @Id + private int id; + } +} + + diff --git a/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/util/GenericTypeDiscoveryTest.java b/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/util/GenericTypeDiscoveryTest.java index 25136a7e8c..c0f12eed04 100644 --- a/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/util/GenericTypeDiscoveryTest.java +++ b/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/util/GenericTypeDiscoveryTest.java @@ -71,7 +71,7 @@ public class GenericTypeDiscoveryTest extends BaseAnnotationIndexTestCase { info = configuredClass.getClassInfo(); assertEquals( "wrong class", DotName.createSimple( PricedStuff.class.getName() ), info.name() ); assertFalse( - "PricedStuff should not mapped properties", configuredClass.getMappedAttributes().iterator().hasNext() + "PricedStuff should not mapped properties", configuredClass.getSimpleAttributes().iterator().hasNext() ); assertTrue( iter.hasNext() ); @@ -88,7 +88,7 @@ public class GenericTypeDiscoveryTest extends BaseAnnotationIndexTestCase { configuredClass = iter.next(); info = configuredClass.getClassInfo(); assertEquals( "wrong class", DotName.createSimple( Paper.class.getName() ), info.name() ); - assertFalse( "Paper should not mapped properties", configuredClass.getMappedAttributes().iterator().hasNext() ); + assertFalse( "Paper should not mapped properties", configuredClass.getSimpleAttributes().iterator().hasNext() ); assertFalse( iter.hasNext() ); }