HHH-6114 Starting to bind id attributes

This commit is contained in:
Hardy Ferentschik 2011-04-15 23:51:08 +02:00
parent dd9ccddc6e
commit da028ee137
8 changed files with 223 additions and 70 deletions

View File

@ -29,13 +29,19 @@ import org.hibernate.EntityMode;
* Models the notion of an entity
*
* @author Steve Ebersole
* @author Hardy Ferentschik
*/
public class Entity extends AbstractAttributeContainer {
private final PojoEntitySpecifics pojoEntitySpecifics = new PojoEntitySpecifics();
private final Dom4jEntitySpecifics dom4jEntitySpecifics = new Dom4jEntitySpecifics();
private final MapEntitySpecifics mapEntitySpecifics = new MapEntitySpecifics();
/**
* Constructor for the entity
*
* @param name the name of the entity
* @param superType the super type for this entity. If there is not super type {@code null} needs to be passed.
*/
public Entity(String name, Hierarchical superType) {
super( name, superType );
}
@ -61,6 +67,7 @@ public class Entity extends AbstractAttributeContainer {
public static interface EntityModeEntitySpecifics {
public EntityMode getEntityMode();
public String getTuplizerClassName();
}

View File

@ -26,14 +26,12 @@ package org.hibernate.metamodel.source.annotations;
import java.util.Iterator;
import java.util.Set;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.Index;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.metamodel.source.annotations.util.ConfiguredClassHierarchyBuilder;
import org.hibernate.metamodel.source.annotations.util.JandexHelper;
import org.hibernate.metamodel.source.internal.MetadataImpl;
/**
@ -70,34 +68,9 @@ public class AnnotationBinder {
}
public void bindEntity(ConfiguredClass entity) {
ClassInfo classInfo = entity.getClassInfo();
log.info( "Binding entity from annotated class: {}", entity.getName() );
EntityBinder entityBinder = new EntityBinder( metadata, entity );
//@Entity and @MappedSuperclass on the same class leads to a NPE down the road
AnnotationInstance jpaEntityAnnotation = JandexHelper.getSingleAnnotation( classInfo, JPADotNames.ENTITY );
AnnotationInstance mappedSuperClassAnnotation = JandexHelper.getSingleAnnotation(
classInfo, JPADotNames.MAPPED_SUPER_CLASS
);
AnnotationInstance hibernateEntityAnnotation = JandexHelper.getSingleAnnotation(
classInfo, HibernateDotNames.ENTITY
);
// //TODO: be more strict with secondarytable allowance (not for ids, not for secondary table join columns etc)
// InheritanceState inheritanceState = inheritanceStatePerClass.get( clazzToProcess );
// AnnotatedClassType classType = mappings.getClassType( clazzToProcess );
//
// //Queries declared in MappedSuperclass should be usable in Subclasses
// if ( AnnotatedClassType.EMBEDDABLE_SUPERCLASS.equals( classType ) ) {
// bindQueries( clazzToProcess, mappings );
// bindTypeDefs( clazzToProcess, mappings );
// bindFilterDefs( clazzToProcess, mappings );
// }
//
// if ( !isEntityClassType( clazzToProcess, classType ) ) {
// return;
// }
//
log.info( "Binding entity from annotated class: {}", classInfo.name() );
//
// PersistentClass superEntity = getSuperEntity(
// clazzToProcess, inheritanceStatePerClass, mappings, inheritanceState
@ -106,9 +79,8 @@ public class AnnotationBinder {
// PersistentClass persistentClass = makePersistentClass( inheritanceState, superEntity );
EntityBinder entityBinder = new EntityBinder(
metadata, classInfo, jpaEntityAnnotation, hibernateEntityAnnotation
);
// entityBinder.setInheritanceState( inheritanceState );
//
// bindQueries( clazzToProcess, mappings );

View File

@ -52,42 +52,47 @@ import org.hibernate.service.classloading.spi.ClassLoaderService;
* @author Hardy Ferentschik
*/
public class ConfiguredClass {
private final ConfiguredClass parent;
private final ClassInfo classInfo;
private final Class<?> clazz;
private final boolean isRoot;
private final AccessType classAccessType;
private final ConfiguredClassHierarchy hierarchy;
private final AccessType hierarchyAccessType;
private final boolean isMappedSuperClass;
private final List<MappedProperty> mappedProperties;
public ConfiguredClass(ClassInfo info, ConfiguredClassHierarchy hierarchy, ServiceRegistry serviceRegistry) {
public ConfiguredClass(ClassInfo info, ConfiguredClass parent, AccessType hierarchyAccessType, ServiceRegistry serviceRegistry) {
this.classInfo = info;
this.hierarchy = hierarchy;
this.parent = parent;
this.isRoot = parent == null;
this.hierarchyAccessType = hierarchyAccessType;
//@Entity and @MappedSuperclass on the same class leads to a NPE down the road
AnnotationInstance jpaEntityAnnotation = JandexHelper.getSingleAnnotation( classInfo, JPADotNames.ENTITY );
AnnotationInstance mappedSuperClassAnnotation = JandexHelper.getSingleAnnotation(
classInfo, JPADotNames.MAPPED_SUPER_CLASS
);
if ( jpaEntityAnnotation != null && mappedSuperClassAnnotation != null ) {
throw new AnnotationException(
"An entity cannot be annotated with both @Entity and @MappedSuperclass: "
+ classInfo.name().toString()
);
}
AnnotationInstance mappedSuperClassAnnotation = assertNotEntityAndMAppedSuperClass();
this.clazz = serviceRegistry.getService( ClassLoaderService.class ).classForName( info.toString() );
isMappedSuperClass = mappedSuperClassAnnotation != null;
classAccessType = determineClassAccessType( hierarchy.getDefaultAccessType() );
classAccessType = determineClassAccessType();
mappedProperties = collectMappedProperties();
// make sure the properties are ordered by property name
Collections.sort( mappedProperties );
}
public String getName() {
return clazz.getName();
}
public ClassInfo getClassInfo() {
return classInfo;
}
public ConfiguredClass getParent() {
return parent;
}
public boolean isRoot() {
return isRoot;
}
public boolean isMappedSuperClass() {
return isMappedSuperClass;
}
@ -101,7 +106,7 @@ public class ConfiguredClass {
return sb.toString();
}
private AccessType determineClassAccessType(AccessType hierarchyAccessType) {
private AccessType determineClassAccessType() {
// default to the hierarchy access type to start with
AccessType accessType = hierarchyAccessType;
@ -231,6 +236,22 @@ public class ConfiguredClass {
}
}
}
private AnnotationInstance assertNotEntityAndMAppedSuperClass() {
//@Entity and @MappedSuperclass on the same class leads to a NPE down the road
AnnotationInstance jpaEntityAnnotation = JandexHelper.getSingleAnnotation( classInfo, JPADotNames.ENTITY );
AnnotationInstance mappedSuperClassAnnotation = JandexHelper.getSingleAnnotation(
classInfo, JPADotNames.MAPPED_SUPER_CLASS
);
if ( jpaEntityAnnotation != null && mappedSuperClassAnnotation != null ) {
throw new AnnotationException(
"An entity cannot be annotated with both @Entity and @MappedSuperclass: "
+ classInfo.name().toString()
);
}
return mappedSuperClassAnnotation;
}
}

View File

@ -57,8 +57,11 @@ public class ConfiguredClassHierarchy implements Iterable<ConfiguredClass> {
inheritanceType = determineInheritanceType( classes );
configuredClasses = new ArrayList<ConfiguredClass>();
ConfiguredClass parent = null;
for ( ClassInfo info : classes ) {
configuredClasses.add( new ConfiguredClass( info, this, serviceRegistry ) );
ConfiguredClass configuredClass = new ConfiguredClass( info, parent, defaultAccessType, serviceRegistry );
configuredClasses.add( configuredClass );
parent = configuredClass;
}
}

View File

@ -23,30 +23,89 @@
*/
package org.hibernate.metamodel.source.annotations;
import java.util.List;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.ClassInfo;
import org.hibernate.AssertionFailure;
import org.hibernate.MappingException;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.metamodel.binding.EntityBinding;
import org.hibernate.metamodel.binding.SimpleAttributeBinding;
import org.hibernate.metamodel.domain.Entity;
import org.hibernate.metamodel.domain.Hierarchical;
import org.hibernate.metamodel.source.annotations.util.JandexHelper;
import org.hibernate.metamodel.source.internal.MetadataImpl;
/**
* Creates the domain and relational metamodel for a configured class and binds them together.
*
* @author Hardy Ferentschik
*/
public class EntityBinder {
private final ClassInfo classToBind;
private final ConfiguredClass entity;
private final MetadataImpl meta;
public EntityBinder(MetadataImpl metadata, ClassInfo classInfo, AnnotationInstance jpaEntityAnnotation, AnnotationInstance hibernateEntityAnnotation) {
this.classToBind = classInfo;
public EntityBinder(MetadataImpl metadata, ConfiguredClass configuredClass) {
this.entity = configuredClass;
this.meta = metadata;
EntityBinding entityBinding = new EntityBinding();
bindJpaAnnotation( jpaEntityAnnotation, entityBinding );
bindHibernateAnnotation( hibernateEntityAnnotation, entityBinding );
metadata.addEntity( entityBinding );
bindJpaEntityAnnotation( entityBinding );
// we also have to take care of an optional Hibernate specific @Id
bindHibernateEntityAnnotation( entityBinding );
if ( configuredClass.isRoot() ) {
bindId( entityBinding );
}
meta.addEntity( entityBinding );
}
private void bindHibernateAnnotation(AnnotationInstance annotation, EntityBinding entityBinding) {
private void bindId(EntityBinding entityBinding) {
switch ( determineIdType() ) {
case SIMPLE: {
bindSingleIdAnnotation( entityBinding );
break;
}
case COMPOSED: {
// todo
break;
}
case EMBEDDED: {
// todo
break;
}
default: {
}
}
}
private void bindJpaEntityAnnotation(EntityBinding entityBinding) {
AnnotationInstance jpaEntityAnnotation = JandexHelper.getSingleAnnotation(
entity.getClassInfo(), JPADotNames.ENTITY
);
String name;
if ( jpaEntityAnnotation.value( "name" ) == null ) {
name = StringHelper.unqualify( entity.getName() );
}
else {
name = jpaEntityAnnotation.value( "name" ).asString();
}
entityBinding.setEntity( new Entity( name, getSuperType() ) );
}
private void bindSingleIdAnnotation(EntityBinding entityBinding) {
AnnotationInstance idAnnotation = JandexHelper.getSingleAnnotation(
entity.getClassInfo(), JPADotNames.ID
);
String idName = JandexHelper.getPropertyName( idAnnotation.target() );
entityBinding.getEntity().getOrCreateSingularAttribute( idName );
SimpleAttributeBinding idBinding = entityBinding.makeSimplePrimaryKeyAttributeBinding( idName );
}
private void bindHibernateEntityAnnotation(EntityBinding entityBinding) {
AnnotationInstance hibernateEntityAnnotation = JandexHelper.getSingleAnnotation(
entity.getClassInfo(), HibernateDotNames.ENTITY
);
// if ( hibAnn != null ) {
// dynamicInsert = hibAnn.dynamicInsert();
// dynamicUpdate = hibAnn.dynamicUpdate();
@ -66,18 +125,64 @@ public class EntityBinder {
// }
}
private void bindJpaAnnotation(AnnotationInstance annotation, EntityBinding entityBinding) {
if ( annotation == null ) {
throw new AssertionFailure( "@Entity cannot be not null when binding an entity" );
private Hierarchical getSuperType() {
ConfiguredClass parent = entity.getParent();
if ( parent == null ) {
return null;
}
String name;
if ( annotation.value( "name" ) == null ) {
name = StringHelper.unqualify( classToBind.name().toString() );
EntityBinding parentBinding = meta.getEntityBinding( parent.getName() );
if ( parentBinding == null ) {
throw new AssertionFailure(
"Parent entity " + parent.getName() + " of entity " + entity.getName() + "not yet created!"
);
}
else {
name = annotation.value( "name" ).asString();
return parentBinding.getEntity();
}
private IdType determineIdType() {
List<AnnotationInstance> idAnnotations = entity.getClassInfo().annotations().get( JPADotNames.ENTITY );
List<AnnotationInstance> embeddedIdAnnotations = entity.getClassInfo()
.annotations()
.get( JPADotNames.EMBEDDED_ID );
if ( idAnnotations != null && embeddedIdAnnotations != null ) {
throw new MappingException(
"@EmbeddedId and @Id cannot be used together. Check the configuration for " + entity.getName() + "."
);
}
entityBinding.setEntity( new Entity( name, null ) );
if ( embeddedIdAnnotations != null ) {
if ( embeddedIdAnnotations.size() == 1 ) {
return IdType.EMBEDDED;
}
else {
throw new MappingException( "Multiple @EmbeddedId annotations are not allowed" );
}
}
if ( idAnnotations != null ) {
if ( idAnnotations.size() == 1 ) {
return IdType.SIMPLE;
}
else {
return IdType.COMPOSED;
}
}
return IdType.NONE;
}
enum IdType {
// single @Id annotation
SIMPLE,
// multiple @Id annotations
COMPOSED,
// @EmbeddedId annotation
EMBEDDED,
// does not contain any identifier mappings
NONE
}
}

View File

@ -24,6 +24,7 @@
package org.hibernate.metamodel.source.annotations;
import javax.persistence.Access;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Inheritance;
@ -43,6 +44,7 @@ public interface JPADotNames {
public static final DotName INHERITANCE = DotName.createSimple( Inheritance.class.getName() );
public static final DotName ID = DotName.createSimple( Id.class.getName() );
public static final DotName EMBEDDED_ID = DotName.createSimple( EmbeddedId.class.getName() );
public static final DotName ACCESS = DotName.createSimple( Access.class.getName() );
public static final DotName TRANSIENT = DotName.createSimple( Transient.class.getName() );
}

View File

@ -42,6 +42,9 @@ import org.hibernate.metamodel.source.internal.MetadataImpl;
* @author Hardy Ferentschik
*/
public class FetchProfileBinder {
private FetchProfileBinder() {
}
/**
* Binds all {@link org.hibernate.annotations.FetchProfiles} and {@link org.hibernate.annotations.FetchProfile} \
* annotations to the specified meta data instance.

View File

@ -23,15 +23,20 @@
*/
package org.hibernate.metamodel.source.annotations.util;
import java.beans.Introspector;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.FieldInfo;
import org.jboss.jandex.Index;
import org.jboss.jandex.Indexer;
import org.jboss.jandex.MethodInfo;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
@ -46,6 +51,40 @@ public class JandexHelper {
private JandexHelper() {
}
/**
* Expects a method or field annotation target and returns the property name for this target
*
* @param target the annotation target
*
* @return the property name of the target. For a field it is the field name and for a method name it is
* the method name stripped of 'is', 'has' or 'get'
*/
public static String getPropertyName(AnnotationTarget target) {
if ( !( target instanceof MethodInfo || target instanceof FieldInfo ) ) {
throw new AssertionFailure( "Unexpected annotation target " + target.toString() );
}
if ( target instanceof FieldInfo ) {
return ( (FieldInfo) target ).name();
}
else {
String methodName = ( (MethodInfo) target ).name();
if ( methodName.startsWith( "is" ) ) {
methodName = Introspector.decapitalize( methodName.substring( 2 ) );
}
else if ( methodName.startsWith( "has" ) ) {
methodName = Introspector.decapitalize( methodName.substring( 3 ) );
}
else if ( methodName.startsWith( "get" ) ) {
methodName = Introspector.decapitalize( methodName.substring( 3 ) );
}
else {
throw new AssertionFailure( "Expected a method following the Java Bean notation" );
}
return methodName;
}
}
/**
* @param classInfo the class info from which to retrieve the annotation instance
* @param annotationName the annotation to retrieve from the class info
@ -75,8 +114,9 @@ public class JandexHelper {
/**
* Creates a jandex index for the specified classes
*
* @param classLoaderService class loader service
* @param classLoaderService class loader service
* @param classes the classes to index
*
* @return an annotation repository w/ all the annotation discovered in the specified classes
*/
public static Index indexForClass(ClassLoaderService classLoaderService, Class<?>... classes) {
@ -100,7 +140,7 @@ public class JandexHelper {
count++;
}
builder.append( "]" );
throw new HibernateException( "Unable to create annotation index for " + builder.toString());
throw new HibernateException( "Unable to create annotation index for " + builder.toString() );
}
}
return indexer.complete();