HHH-6262 bind @EmbeddedId
This commit is contained in:
parent
88f69a135b
commit
793f317ea9
|
@ -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() {
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue