From 793f317ea97f31eed9eeec8919da303786e29aba Mon Sep 17 00:00:00 2001 From: Strong Liu Date: Fri, 1 Jul 2011 00:31:17 +0800 Subject: [PATCH] HHH-6262 bind @EmbeddedId --- .../metamodel/binding/EntityIdentifier.java | 5 +- .../AbstractTableSpecification.java | 2 +- .../metamodel/relational/InLineView.java | 4 +- .../hibernate/metamodel/relational/Table.java | 14 +++-- .../relational/TableSpecification.java | 2 +- .../annotations/attribute/AttributeType.java | 1 + .../attribute/SimpleAttribute.java | 3 +- .../annotations/entity/ConfiguredClass.java | 57 ++++++++++++------- .../entity/ConfiguredClassHierarchy.java | 36 +++++++++--- .../annotations/entity/EntityBinder.java | 47 ++++++++++++--- .../annotations/entity/EmbeddedIdTests.java | 51 +++++++++++++++++ 11 files changed, 174 insertions(+), 48 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/entity/EmbeddedIdTests.java diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/binding/EntityIdentifier.java b/hibernate-core/src/main/java/org/hibernate/metamodel/binding/EntityIdentifier.java index 051368dcf1..97dba55bb5 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/binding/EntityIdentifier.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/binding/EntityIdentifier.java @@ -48,8 +48,7 @@ public class EntityIdentifier { private SimpleAttributeBinding attributeBinding; private IdentifierGenerator identifierGenerator; private IdGenerator idGenerator; - private boolean isEmbedded; - private boolean isIdentifierMapper; + private boolean isIdentifierMapper = false; // todo : mappers, etc /** @@ -79,7 +78,7 @@ public class EntityIdentifier { } public boolean isEmbedded() { - return isEmbedded; + return attributeBinding.getValuesSpan()>0; } public boolean isIdentifierMapper() { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/AbstractTableSpecification.java b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/AbstractTableSpecification.java index bd76dbbd4f..6163c34f15 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/AbstractTableSpecification.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/AbstractTableSpecification.java @@ -36,7 +36,7 @@ import java.util.concurrent.atomic.AtomicInteger; * * @author Steve Ebersole */ -public abstract class AbstractTableSpecification implements TableSpecification, ValueContainer { +public abstract class AbstractTableSpecification implements TableSpecification { private final static AtomicInteger tableCounter = new AtomicInteger( 0 ); private final int tableNumber; private final LinkedHashMap values = new LinkedHashMap(); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/InLineView.java b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/InLineView.java index 7872e94e76..b37a72e8ca 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/InLineView.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/InLineView.java @@ -32,7 +32,7 @@ import java.util.Collections; * @author Gavin King * @author Steve Ebersole */ -public class InLineView extends AbstractTableSpecification implements ValueContainer { +public class InLineView extends AbstractTableSpecification { private final Schema schema; private final String logicalName; private final String select; @@ -77,7 +77,7 @@ public class InLineView extends AbstractTableSpecification implements ValueConta } @Override - public Iterable getCheckConstraints() { + public Iterable getCheckConstraints() { return Collections.emptyList(); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java index 190b790fb9..512aa85f03 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/Table.java @@ -29,20 +29,22 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Set; +import org.hibernate.internal.util.StringHelper; + /** * Models the concept of a relational TABLE (or VIEW). * * @author Gavin King * @author Steve Ebersole */ -public class Table extends AbstractTableSpecification implements ValueContainer, Exportable { +public class Table extends AbstractTableSpecification implements Exportable { private final Schema database; private final Identifier tableName; private final String qualifiedName; private LinkedHashMap indexes; private LinkedHashMap uniqueKeys; - private List checkConstraints; + private List checkConstraints; private Set comments; public Table(Schema database, String tableName) { @@ -115,16 +117,18 @@ public class Table extends AbstractTableSpecification implements ValueContainer, } @Override - public Iterable getCheckConstraints() { + public Iterable getCheckConstraints() { return checkConstraints; } @Override public void addCheckConstraint(String checkCondition) { if ( checkConstraints == null ) { - checkConstraints = new ArrayList(); + checkConstraints = new ArrayList(); } - checkConstraints.add( checkCondition ); + //todo ? StringHelper.isEmpty( checkCondition ); + //todo default name? + checkConstraints.add( new CheckConstraint( this, "", checkCondition ) ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/TableSpecification.java b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/TableSpecification.java index 8e78bf45fe..39dc2ff564 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/relational/TableSpecification.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/relational/TableSpecification.java @@ -89,7 +89,7 @@ public interface TableSpecification extends ValueContainer, Loggable { public UniqueKey getOrCreateUniqueKey(String name); - public Iterable getCheckConstraints(); + public Iterable getCheckConstraints(); public void addCheckConstraint(String checkCondition); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/attribute/AttributeType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/attribute/AttributeType.java index 0b4c67d7dc..c8f250be21 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/attribute/AttributeType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/attribute/AttributeType.java @@ -39,6 +39,7 @@ public enum AttributeType { MANY_TO_ONE( JPADotNames.MANY_TO_ONE ), MANY_TO_MANY( JPADotNames.MANY_TO_MANY ), ELEMENT_COLLECTION( JPADotNames.ELEMENT_COLLECTION ), + EMBEDDED_ID( JPADotNames.EMBEDDED_ID ), EMBEDDED( JPADotNames.EMBEDDED ); private final DotName annotationDotName; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/attribute/SimpleAttribute.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/attribute/SimpleAttribute.java index 6fb82355f1..75c36f4ac7 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/attribute/SimpleAttribute.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/attribute/SimpleAttribute.java @@ -130,7 +130,8 @@ public class SimpleAttribute extends MappedAttribute { AnnotationInstance idAnnotation = JandexHelper.getSingleAnnotation( annotations, JPADotNames.ID ); - isId = idAnnotation != null; + AnnotationInstance embeddedIdAnnotation = JandexHelper.getSingleAnnotation( annotations, JPADotNames.EMBEDDED_ID ); + isId = !(idAnnotation == null && embeddedIdAnnotation == null); AnnotationInstance versionAnnotation = JandexHelper.getSingleAnnotation( annotations, JPADotNames.VERSION ); isVersioned = versionAnnotation != null; 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 0048175a11..ec85c4d2c7 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 @@ -393,30 +393,16 @@ public class ConfiguredClass { attribute = SimpleAttribute.createSimpleAttribute( attributeName, type.getName(), annotations ); break; } - case EMBEDDED: { - ClassInfo embeddableClassInfo = context.getClassInfo( type.getName() ); - if ( classInfo == null ) { - String msg = String.format( - "Attribute %s of entity %s is annotated with @Embedded, but no embeddable configuration for type %s can be found.", - attributeName, - getName(), - type.getName() - ); - throw new AnnotationException( msg ); - } + case ELEMENT_COLLECTION: + case EMBEDDED_ID: - context.resolveAllTypes( type.getName() ); - ConfiguredClassHierarchy hierarchy = ConfiguredClassHierarchyBuilder.createEmbeddableHierarchy( - context.loadClass( embeddableClassInfo.toString() ), - classAccessType, - context - ); - embeddedClasses.put( attributeName, hierarchy.getLeaf() ); + case EMBEDDED: { + resolveEmbeddable( attributeName, type ); } // TODO handle the different association types default: { attribute = AssociationAttribute.createAssociationAttribute( - attributeName, ( (Class) type ).getName(), attributeType, annotations + attributeName,type.getName(), attributeType, annotations ); } } @@ -424,7 +410,28 @@ public class ConfiguredClass { return attribute; } - /** + private void resolveEmbeddable(String attributeName, Class type) { + ClassInfo embeddableClassInfo = context.getClassInfo( type.getName() ); + if ( classInfo == null ) { + String msg = String.format( + "Attribute %s of entity %s is annotated with @Embedded, but no embeddable configuration for type %s can be found.", + attributeName, + getName(), + type.getName() + ); + throw new AnnotationException( msg ); + } + + context.resolveAllTypes( type.getName() ); + ConfiguredClassHierarchy hierarchy = ConfiguredClassHierarchyBuilder.createEmbeddableHierarchy( + context.loadClass( embeddableClassInfo.toString() ), + classAccessType, + context + ); + embeddedClasses.put( attributeName, hierarchy.getLeaf() ); + } + + /** * Given the annotations defined on a persistent attribute this methods determines the attribute type. * * @param annotations the annotations defined on the persistent attribute @@ -460,6 +467,16 @@ public class ConfiguredClass { discoveredAttributeTypes.put( AttributeType.EMBEDDED, embedded ); } + AnnotationInstance embeddIded = JandexHelper.getSingleAnnotation( annotations, JPADotNames.EMBEDDED_ID ); + if ( embeddIded != null ) { + discoveredAttributeTypes.put( AttributeType.EMBEDDED_ID, embeddIded ); + } + + AnnotationInstance elementCollection = JandexHelper.getSingleAnnotation( annotations, JPADotNames.ELEMENT_COLLECTION ); + if ( elementCollection != null ) { + discoveredAttributeTypes.put( AttributeType.ELEMENT_COLLECTION, elementCollection ); + } + if ( discoveredAttributeTypes.size() == 0 ) { return AttributeType.BASIC; } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/ConfiguredClassHierarchy.java b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/ConfiguredClassHierarchy.java index c74923e4c2..b5ac1d36ed 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/ConfiguredClassHierarchy.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/source/annotations/entity/ConfiguredClassHierarchy.java @@ -144,20 +144,40 @@ public class ConfiguredClassHierarchy implements Iter * annotations. */ private static AccessType determineDefaultAccessType(List classes) { - AccessType accessType = null; + AccessType defaultAccessType = null; + AccessType accessTypeByIdPlacement = null; for ( ClassInfo info : classes ) { List idAnnotations = info.annotations().get( JPADotNames.ID ); - if ( idAnnotations == null || idAnnotations.size() == 0 ) { - continue; + List accessAnnotations = info.annotations().get( JPADotNames.ACCESS ); + + if ( accessAnnotations != null && !accessAnnotations.isEmpty() ) { + for ( AnnotationInstance annotation : accessAnnotations ) { + if ( annotation.target() instanceof ClassInfo ) { + defaultAccessType = JandexHelper.getValueAsEnum( annotation, "value", AccessType.class ); + break; //there can be only one @Access on class level. + } + } + } + if ( idAnnotations != null && !idAnnotations.isEmpty() ) { + accessTypeByIdPlacement = determineAccessTypeByIdPlacement( idAnnotations ); } - accessType = determineAccessTypeByIdPlacement( idAnnotations ); } + if ( defaultAccessType != null ) { + return defaultAccessType; + } else if (accessTypeByIdPlacement != null ){ + return accessTypeByIdPlacement; + } else { + return AccessType.PROPERTY; + } - if ( accessType == null ) { - return throwIdNotFoundAnnotationException( classes ); - } - return accessType; +// +// +// if ( accessType == null ) { +// return throwIdNotFoundAnnotationException( classes ); +// } +// +// return accessType; } private static AccessType determineAccessTypeByIdPlacement(List idAnnotations) { 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 4939030913..5b68f836e7 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 @@ -42,6 +42,7 @@ import org.hibernate.annotations.ResultCheckStyle; import org.hibernate.cache.spi.RegionFactory; import org.hibernate.cache.spi.access.AccessType; import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; +import org.hibernate.id.IdentifierGenerator; import org.hibernate.internal.util.StringHelper; import org.hibernate.metamodel.binding.Caching; import org.hibernate.metamodel.binding.CustomSQL; @@ -119,15 +120,15 @@ public class EntityBinder { bindInheritance( entityBinding ); - // take care of the id, attributes and relations - if ( entityClass.isRoot() ) { - bindId( entityBinding ); - } + // bind all attributes - simple as well as associations bindAttributes( entityBinding ); bindEmbeddedAttributes( entityBinding ); - + // take care of the id, attributes and relations + if ( entityClass.isRoot() ) { + bindId( entityBinding ); + } bindTableUniqueConstraints( entityBinding ); @@ -452,7 +453,7 @@ public class EntityBinder { break; } case EMBEDDED: { - // todo + bindEmbeddedIdAnnotation( entityBinding ); break; } default: { @@ -475,6 +476,37 @@ 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) { AnnotationInstance idAnnotation = JandexHelper.getSingleAnnotation( entityClass.getClassInfo(), JPADotNames.ID @@ -548,10 +580,11 @@ public class EntityBinder { ) ); } - else { + if( idGenerator == null ) { idGenerator = new IdGenerator( "NAME", strategy, new HashMap() ); entityBinding.getEntityIdentifier().setIdGenerator( idGenerator ); } + entityBinding.getEntityIdentifier().createIdentifierGenerator( meta.getIdentifierGeneratorFactory() ); } private void bindAttributes(EntityBinding entityBinding) { diff --git a/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/entity/EmbeddedIdTests.java b/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/entity/EmbeddedIdTests.java new file mode 100644 index 0000000000..21c77c743d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/metamodel/source/annotations/entity/EmbeddedIdTests.java @@ -0,0 +1,51 @@ +package org.hibernate.metamodel.source.annotations.entity; + +import javax.persistence.Access; +import javax.persistence.AccessType; +import javax.persistence.Embeddable; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; + +import org.junit.Test; + +import org.hibernate.id.Assigned; +import org.hibernate.metamodel.binding.EntityBinding; +import org.hibernate.metamodel.binding.EntityIdentifier; +import org.hibernate.metamodel.domain.Attribute; +import org.hibernate.metamodel.domain.Component; + +import static junit.framework.Assert.assertTrue; + +/** + * @author Strong Liu + */ +public class EmbeddedIdTests extends BaseAnnotationBindingTestCase { + @Test + public void testEmbeddable() { + buildMetadataSources( User.class, Address.class ); + EntityBinding binding = getEntityBinding( User.class ); + EntityIdentifier identifier = binding.getEntityIdentifier(); + assertTrue( identifier.isEmbedded() ); + assertTrue( + "EmbeddedId generator should be 'assigned'", identifier.getIdentifierGenerator() instanceof Assigned + ); + } + + @Entity + @Access( AccessType.FIELD ) + class User { + private String name; + @EmbeddedId + private Address address; + } + + @Embeddable + class Address { + String street; + String city; + String postCode; + } +} + + +