HHH-6262 bind @EmbeddedId

This commit is contained in:
Strong Liu 2011-07-01 00:31:17 +08:00
parent 88f69a135b
commit 793f317ea9
11 changed files with 174 additions and 48 deletions

View File

@ -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() {

View File

@ -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<String,SimpleValue> values = new LinkedHashMap<String,SimpleValue>();

View File

@ -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<String> getCheckConstraints() {
public Iterable<CheckConstraint> getCheckConstraints() {
return Collections.emptyList();
}

View File

@ -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 <tt>TABLE</tt> (or <tt>VIEW</tt>).
*
* @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<String,Index> indexes;
private LinkedHashMap<String,UniqueKey> uniqueKeys;
private List<String> checkConstraints;
private List<CheckConstraint> checkConstraints;
private Set<String> comments;
public Table(Schema database, String tableName) {
@ -115,16 +117,18 @@ public class Table extends AbstractTableSpecification implements ValueContainer,
}
@Override
public Iterable<String> getCheckConstraints() {
public Iterable<CheckConstraint> getCheckConstraints() {
return checkConstraints;
}
@Override
public void addCheckConstraint(String checkCondition) {
if ( checkConstraints == null ) {
checkConstraints = new ArrayList<String>();
checkConstraints = new ArrayList<CheckConstraint>();
}
checkConstraints.add( checkCondition );
//todo ? StringHelper.isEmpty( checkCondition );
//todo default name?
checkConstraints.add( new CheckConstraint( this, "", checkCondition ) );
}
@Override

View File

@ -89,7 +89,7 @@ public interface TableSpecification extends ValueContainer, Loggable {
public UniqueKey getOrCreateUniqueKey(String name);
public Iterable<String> getCheckConstraints();
public Iterable<CheckConstraint> getCheckConstraints();
public void addCheckConstraint(String checkCondition);

View File

@ -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;

View File

@ -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;

View File

@ -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<EmbeddableClass> 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<EmbeddableClass> 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;
}

View File

@ -144,20 +144,40 @@ public class ConfiguredClassHierarchy<T extends ConfiguredClass> implements Iter
* annotations.
*/
private static AccessType determineDefaultAccessType(List<ClassInfo> classes) {
AccessType accessType = null;
AccessType defaultAccessType = null;
AccessType accessTypeByIdPlacement = null;
for ( ClassInfo info : classes ) {
List<AnnotationInstance> idAnnotations = info.annotations().get( JPADotNames.ID );
if ( idAnnotations == null || idAnnotations.size() == 0 ) {
continue;
List<AnnotationInstance> 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<AnnotationInstance> idAnnotations) {

View File

@ -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<String,String> parms = new HashMap<String, String>( 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<String, String>() );
entityBinding.getEntityIdentifier().setIdGenerator( idGenerator );
}
entityBinding.getEntityIdentifier().createIdentifierGenerator( meta.getIdentifierGeneratorFactory() );
}
private void bindAttributes(EntityBinding entityBinding) {

View File

@ -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;
}
}