[hibernate-commits] Hibernate SVN: r18625 - in core/trunk/annotations/src: test/java/org/hibernate/test/annotations/derivedidentities/e1/b and 2 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Tue Jan 26 07:57:01 EST 2010


Author: epbernard
Date: 2010-01-26 07:57:01 -0500 (Tue, 26 Jan 2010)
New Revision: 18625

Added:
   core/trunk/annotations/src/main/java/org/hibernate/cfg/ColumnsBuilder.java
   core/trunk/annotations/src/main/java/org/hibernate/cfg/CopyIdentifierComponentSecondPass.java
   core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/DerivedIdentityIdClassParentEmbeddedIdDepTest.java
Modified:
   core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java
   core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java
   core/trunk/annotations/src/main/java/org/hibernate/cfg/BinderHelper.java
   core/trunk/annotations/src/main/java/org/hibernate/cfg/InheritanceState.java
   core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/DerivedIdentitySimpleParentEmbeddedIdDepTest.java
   core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/c/Dependent.java
   core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/DependentId.java
   core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/EmployeeId.java
Log:
HHH-4529 Add support for parent's id being an IdClass or an EmbeddedId (last case not tested yet). (example e2.b)

Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java	2010-01-26 12:19:38 UTC (rev 18624)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java	2010-01-26 12:57:01 UTC (rev 18625)
@@ -52,7 +52,6 @@
 import javax.persistence.IdClass;
 import javax.persistence.InheritanceType;
 import javax.persistence.JoinColumn;
-import javax.persistence.JoinColumns;
 import javax.persistence.JoinTable;
 import javax.persistence.ManyToMany;
 import javax.persistence.ManyToOne;
@@ -105,7 +104,6 @@
 import org.hibernate.annotations.Formula;
 import org.hibernate.annotations.GenericGenerator;
 import org.hibernate.annotations.Index;
-import org.hibernate.annotations.JoinColumnsOrFormulas;
 import org.hibernate.annotations.LazyToOne;
 import org.hibernate.annotations.LazyToOneOption;
 import org.hibernate.annotations.ManyToAny;
@@ -167,7 +165,7 @@
 import org.hibernate.persister.entity.SingleTableEntityPersister;
 import org.hibernate.persister.entity.UnionSubclassEntityPersister;
 import org.hibernate.type.TypeFactory;
-import org.hibernate.util.StringHelper;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -182,7 +180,8 @@
  * @author Hardy Ferentschik
  */
 @SuppressWarnings("unchecked")
-public final class AnnotationBinder {
+public final class
+		AnnotationBinder {
 
 	/*
 	 * Some design description
@@ -625,30 +624,16 @@
 		HashMap<String, IdGenerator> classGenerators = buildLocalGenerators( clazzToProcess, mappings );
 
 		// check properties
-		final ElementsToProcess elementsToProcess = getElementsToProcess(
-				persistentClass, clazzToProcess, inheritanceStatePerClass, entityBinder, mappings
-		);
-		
+		final InheritanceState.ElementsToProcess elementsToProcess = inheritanceState.getElementsToProcess();
+		inheritanceState.postProcess( persistentClass, entityBinder );
+
 		final boolean subclassAndSingleTableStrategy = inheritanceState.getType() == InheritanceType.SINGLE_TABLE
 				&& inheritanceState.hasParents();
 		//process idclass if any
 		Set<String> idProperties = new HashSet<String>();
-		IdClass idClass = null;
-		XClass current = null;
-		if ( !inheritanceState.hasParents() ) {
-			//look for idClass
-			InheritanceState state = inheritanceState;
-			do {
-				current = state.getClazz();
-				if ( current.isAnnotationPresent( IdClass.class ) ) {
-					idClass = current.getAnnotation( IdClass.class );
-					break;
-				}
-				state = InheritanceState.getSuperclassInheritanceState( current, inheritanceStatePerClass );
-			}
-			while ( state != null );
-		}
-		if ( idClass != null ) {
+		XClass classWithIdClass = inheritanceState.getClassWithIdClass(false);
+		if ( classWithIdClass != null ) {
+			IdClass idClass = classWithIdClass.getAnnotation( IdClass.class );
 			XClass compositeClass = mappings.getReflectionManager().toXClass( idClass.value() );
 			boolean isComponent = true;
 			AccessType propertyAccessor = entityBinder.getPropertyAccessor( compositeClass );
@@ -658,7 +643,7 @@
 					entityBinder.getPropertyAccessType(), "id", compositeClass
 			);
 			PropertyData baseInferredData = new PropertyPreloadedData(
-                  entityBinder.getPropertyAccessType(), "id", current
+                  entityBinder.getPropertyAccessType(), "id", classWithIdClass
             );
 			HashMap<String, IdGenerator> localGenerators = new HashMap<String, IdGenerator>();
 			boolean ignoreIdAnnotations = entityBinder.isIgnoreIdAnnotations();
@@ -982,134 +967,127 @@
 		return true;
 	}
 
-	/*
-	 * Get the annotated elements, guessing the access type from @Id or @EmbeddedId presence.
-	 * Change EntityBinder by side effect
-	 */
-	private static ElementsToProcess getElementsToProcess(
-			PersistentClass persistentClass, XClass clazzToProcess,
-			Map<XClass, InheritanceState> inheritanceStatePerClass,
-			EntityBinder entityBinder, ExtendedMappings mappings
-	) {
-		InheritanceState inheritanceState = inheritanceStatePerClass.get( clazzToProcess );
-		assert !inheritanceState.isEmbeddableSuperclass();
+//	/*
+//	 * Get the annotated elements, guessing the access type from @Id or @EmbeddedId presence.
+//	 * Change EntityBinder by side effect
+//	 */
+//	private static InheritanceState.ElementsToProcess getElementsToProcess(
+//			PersistentClass persistentClass, XClass clazzToProcess,
+//			Map<XClass, InheritanceState> inheritanceStatePerClass,
+//			EntityBinder entityBinder, ExtendedMappings mappings
+//	) {
+//		InheritanceState inheritanceState = inheritanceStatePerClass.get( clazzToProcess );
+//		assert !inheritanceState.isEmbeddableSuperclass();
+//
+//
+//		List<XClass> classesToProcess = getMappedSuperclassesTillNextEntityOrdered(
+//				persistentClass, clazzToProcess, inheritanceStatePerClass, mappings
+//		);
+//
+//		AccessType accessType = determineDefaultAccessType( clazzToProcess, inheritanceStatePerClass );
+//
+//		List<PropertyData> elements = new ArrayList<PropertyData>();
+//		int deep = classesToProcess.size();
+//		int idPropertyCount = 0;
+//
+//		for ( int index = 0; index < deep; index++ ) {
+//			PropertyContainer propertyContainer = new PropertyContainer( classesToProcess.get( index ), clazzToProcess );
+//			int currentIdPropertyCount = addElementsOfClass( elements, accessType, propertyContainer, mappings );
+//			idPropertyCount +=  currentIdPropertyCount;
+//		}
+//
+//		entityBinder.setPropertyAccessType( accessType );
+//
+//		if ( idPropertyCount == 0 && !inheritanceState.hasParents() ) {
+//			throw new AnnotationException( "No identifier specified for entity: " + clazzToProcess.getName() );
+//		}
+//
+//		return new InheritanceState.ElementsToProcess( elements, idPropertyCount);
+//	}
 
+//	private static AccessType determineDefaultAccessType(XClass annotatedClass, Map<XClass, InheritanceState> inheritanceStatePerClass) {
+//		XClass xclass = annotatedClass;
+//		while ( xclass != null && !Object.class.getName().equals( xclass.getName() ) ) {
+//			if ( xclass.isAnnotationPresent( Entity.class ) || xclass.isAnnotationPresent( MappedSuperclass.class ) ) {
+//				for ( XProperty prop : xclass.getDeclaredProperties( AccessType.PROPERTY.getType() ) ) {
+//					if ( prop.isAnnotationPresent( Id.class ) || prop.isAnnotationPresent( EmbeddedId.class ) ) {
+//						return AccessType.PROPERTY;
+//					}
+//				}
+//				for ( XProperty prop : xclass.getDeclaredProperties( AccessType.FIELD.getType() ) ) {
+//					if ( prop.isAnnotationPresent( Id.class ) || prop.isAnnotationPresent( EmbeddedId.class ) ) {
+//						return AccessType.FIELD;
+//					}
+//				}
+//			}
+//			xclass = xclass.getSuperclass();
+//		}
+//		throw new AnnotationException( "No identifier specified for entity: " + annotatedClass.getName() );
+//	}
+//
+//	private static List<XClass> getMappedSuperclassesTillNextEntityOrdered(
+//			PersistentClass persistentClass, XClass annotatedClass,
+//			Map<XClass, InheritanceState> inheritanceStatePerClass,
+//			ExtendedMappings mappings
+//	) {
+//
+//		//ordered to allow proper messages on properties subclassing
+//		List<XClass> classesToProcess = new ArrayList<XClass>();
+//		XClass currentClassInHierarchy = annotatedClass;
+//		InheritanceState superclassState;
+//		final ReflectionManager reflectionManager = mappings.getReflectionManager();
+//		do {
+//			classesToProcess.add( 0, currentClassInHierarchy );
+//			XClass superClass = currentClassInHierarchy;
+//			do {
+//				superClass = superClass.getSuperclass();
+//				superclassState = inheritanceStatePerClass.get( superClass );
+//			}
+//			while ( superClass != null && !reflectionManager
+//					.equals( superClass, Object.class ) && superclassState == null );
+//
+//			currentClassInHierarchy = superClass;
+//		}
+//		while ( superclassState != null && superclassState.isEmbeddableSuperclass() );
+//		addMappedSuperClassInMetadata(
+//				persistentClass, annotatedClass, inheritanceStatePerClass, mappings, classesToProcess
+//		);
+//
+//
+//		return classesToProcess;
+//	}
+//
+//	private static void addMappedSuperClassInMetadata(PersistentClass persistentClass,
+//													  XClass annotatedClass,
+//													  Map<XClass, InheritanceState> inheritanceStatePerClass,
+//													  ExtendedMappings mappings,
+//													  List<XClass> classesToProcess) {
+//		//add @MappedSuperclass in the metadata
+//		// classes from 0 to n-1 are @MappedSuperclass and should be linked
+//		org.hibernate.mapping.MappedSuperclass mappedSuperclass = null;
+//		final InheritanceState superEntityState =
+//				InheritanceState.getInheritanceStateOfSuperEntity( annotatedClass, inheritanceStatePerClass );
+//		PersistentClass superEntity =
+//				superEntityState != null ?
+//						mappings.getClass( superEntityState.getClazz().getName() ) :
+//						null;
+//		final int lastMappedSuperclass = classesToProcess.size() - 1;
+//		for ( int index = 0 ; index < lastMappedSuperclass ; index++ ) {
+//			org.hibernate.mapping.MappedSuperclass parentSuperclass = mappedSuperclass;
+//			final Class<?> type = mappings.getReflectionManager().toClass( classesToProcess.get( index ) );
+//			//add MAppedSuperclass if not already there
+//			mappedSuperclass = mappings.getMappedSuperclass( type );
+//			if (mappedSuperclass == null) {
+//				mappedSuperclass = new org.hibernate.mapping.MappedSuperclass(parentSuperclass, superEntity );
+//				mappedSuperclass.setMappedClass( type );
+//				mappings.addMappedSuperclass( type, mappedSuperclass );
+//			}
+//		}
+//		if (mappedSuperclass != null) {
+//			persistentClass.setSuperMappedSuperclass(mappedSuperclass);
+//		}
+//	}
 
-		List<XClass> classesToProcess = getMappedSuperclassesTillNextEntityOrdered(
-				persistentClass, clazzToProcess, inheritanceStatePerClass, mappings
-		);
-
-		AccessType accessType = determineDefaultAccessType( clazzToProcess, inheritanceStatePerClass );
-
-		List<PropertyData> elements = new ArrayList<PropertyData>();
-		int deep = classesToProcess.size();
-		int idPropertyCount = 0;
-
-		for ( int index = 0; index < deep; index++ ) {
-			PropertyContainer propertyContainer = new PropertyContainer( classesToProcess.get( index ), clazzToProcess );
-			int currentIdPropertyCount = addElementsOfClass( elements, accessType, propertyContainer, mappings );
-			idPropertyCount +=  currentIdPropertyCount;
-		}
-
-		entityBinder.setPropertyAccessType( accessType );
-
-		if ( idPropertyCount == 0 && !inheritanceState.hasParents() ) {
-			throw new AnnotationException( "No identifier specified for entity: " + clazzToProcess.getName() );
-		}
-
-		return new ElementsToProcess( elements, idPropertyCount);
-	}
-
-	private static final class ElementsToProcess {
-		private final List<PropertyData> properties;
-		private final int idPropertyCount;
-
-		public List<PropertyData> getElements() {
-			return properties;
-		}
-
-		public int getIdPropertyCount() {
-			return idPropertyCount;
-		}
-
-		private ElementsToProcess(List<PropertyData> properties, int idPropertyCount) {
-			this.properties = properties;
-			this.idPropertyCount = idPropertyCount;
-		}
-	}
-
-	private static AccessType determineDefaultAccessType(XClass annotatedClass, Map<XClass, InheritanceState> inheritanceStatePerClass) {
-		XClass xclass = annotatedClass;
-		while ( xclass != null && !Object.class.getName().equals( xclass.getName() ) ) {
-			if ( xclass.isAnnotationPresent( Entity.class ) || xclass.isAnnotationPresent( MappedSuperclass.class ) ) {
-				for ( XProperty prop : xclass.getDeclaredProperties( AccessType.PROPERTY.getType() ) ) {
-					if ( prop.isAnnotationPresent( Id.class ) || prop.isAnnotationPresent( EmbeddedId.class ) ) {
-						return AccessType.PROPERTY;
-					}
-				}
-				for ( XProperty prop : xclass.getDeclaredProperties( AccessType.FIELD.getType() ) ) {
-					if ( prop.isAnnotationPresent( Id.class ) || prop.isAnnotationPresent( EmbeddedId.class ) ) {
-						return AccessType.FIELD;
-					}
-				}
-			}
-			xclass = xclass.getSuperclass();
-		}
-		throw new AnnotationException( "No identifier specified for entity: " + annotatedClass.getName() );
-	}
-
-	private static List<XClass> getMappedSuperclassesTillNextEntityOrdered(
-			PersistentClass persistentClass, XClass annotatedClass,
-			Map<XClass, InheritanceState> inheritanceStatePerClass,
-			ExtendedMappings mappings
-	) {
-
-		//ordered to allow proper messages on properties subclassing
-		List<XClass> classesToProcess = new ArrayList<XClass>();
-		XClass currentClassInHierarchy = annotatedClass;
-		InheritanceState superclassState;
-		final ReflectionManager reflectionManager = mappings.getReflectionManager();
-		do {
-			classesToProcess.add( 0, currentClassInHierarchy );
-			XClass superClass = currentClassInHierarchy;
-			do {
-				superClass = superClass.getSuperclass();
-				superclassState = inheritanceStatePerClass.get( superClass );
-			}
-			while ( superClass != null && !reflectionManager
-					.equals( superClass, Object.class ) && superclassState == null );
-
-			currentClassInHierarchy = superClass;
-		}
-		while ( superclassState != null && superclassState.isEmbeddableSuperclass() );
-
-		//add @MappedSuperclass in the metadata
-		// classes from 0 to n-1 are @MappedSuperclass and should be linked
-		org.hibernate.mapping.MappedSuperclass mappedSuperclass = null;
-		final InheritanceState superEntityState =
-				InheritanceState.getInheritanceStateOfSuperEntity( annotatedClass, inheritanceStatePerClass );
-		PersistentClass superEntity =
-				superEntityState != null ?
-						mappings.getClass( superEntityState.getClazz().getName() ) :
-						null;
-		final int lastMappedSuperclass = classesToProcess.size() - 1;
-		for ( int index = 0 ; index < lastMappedSuperclass ; index++ ) {
-			org.hibernate.mapping.MappedSuperclass parentSuperclass = mappedSuperclass;
-			final Class<?> type = mappings.getReflectionManager().toClass( classesToProcess.get( index ) );
-			//add MAppedSuperclass if not already there
-			mappedSuperclass = mappings.getMappedSuperclass( type );
-			if (mappedSuperclass == null) {
-				mappedSuperclass = new org.hibernate.mapping.MappedSuperclass(parentSuperclass, superEntity );
-				mappedSuperclass.setMappedClass( type );
-				mappings.addMappedSuperclass( type, mappedSuperclass );
-			}
-		}
-		if (mappedSuperclass != null) {
-			persistentClass.setSuperMappedSuperclass(mappedSuperclass);
-		}
-		return classesToProcess;
-	}
-
 	/*
 	 * Process the filters defined on the given class, as well as all filters defined
 	 * on the MappedSuperclass(s) in the inheritance hierarchy
@@ -1235,7 +1213,7 @@
 	 * @return the number of id properties found while iterating the elements of {@code annoatedClass} using
 	 * the determined access strategy, {@code false} otherwise.
 	 */
-	private static int addElementsOfClass(
+	static int addElementsOfClass(
 			List<PropertyData> elements, AccessType defaultAccessType, PropertyContainer propertyContainer, ExtendedMappings mappings
 	) {
 		int idPropertyCounter = 0;
@@ -1295,14 +1273,12 @@
 			EntityBinder entityBinder, boolean isIdentifierMapper,
 			boolean isComponentEmbedded, boolean inSecondPass, ExtendedMappings mappings,
 			Map<XClass, InheritanceState> inheritanceStatePerClass
-	)
-			throws MappingException {
+	) throws MappingException {
 		/**
 		 * inSecondPass can only be used to apply right away the second pass of a composite-element
 		 * Because it's a value type, there is no bidirectional association, hence second pass
 		 * ordering does not matter
 		 */
-		Ejb3Column[] columns = null;
 
 		log.trace(
 				"Processing annotations of {}.{}", propertyHolder.getEntityName(), inferredData.getPropertyName()
@@ -1318,71 +1294,15 @@
 								+ BinderHelper.getPath( propertyHolder, inferredData )
 				);
 			}
-			return;
+			return ;
 		}
-		Ejb3JoinColumn[] joinColumns = buildExplicitJoinColumns(
-				propertyHolder, property, inferredData, entityBinder, mappings
-		);
 
+		ColumnsBuilder columnsBuilder = new ColumnsBuilder(
+				propertyHolder, nullability, property, inferredData, entityBinder, mappings
+		).extractMetadata();
+		Ejb3Column[] columns = columnsBuilder.getColumns();
+		Ejb3JoinColumn[] joinColumns = columnsBuilder.getJoinColumns();
 
-		if ( property.isAnnotationPresent( Column.class ) || property.isAnnotationPresent( Formula.class ) ) {
-			Column ann = property.getAnnotation( Column.class );
-			Formula formulaAnn = property.getAnnotation( Formula.class );
-			columns = Ejb3Column.buildColumnFromAnnotation(
-					new Column[] { ann }, formulaAnn, nullability, propertyHolder, inferredData,
-					entityBinder.getSecondaryTables(), mappings
-			);
-		}
-		else if ( property.isAnnotationPresent( Columns.class ) ) {
-			Columns anns = property.getAnnotation( Columns.class );
-			columns = Ejb3Column.buildColumnFromAnnotation(
-					anns.columns(), null, nullability, propertyHolder, inferredData, entityBinder.getSecondaryTables(),
-					mappings
-			);
-		}
-
-		//set default values if needed
-		if ( joinColumns == null &&
-				( property.isAnnotationPresent( ManyToOne.class )
-						|| property.isAnnotationPresent( OneToOne.class ) )
-				) {
-			joinColumns = buildDefaultJoinColumnsForXToOne(
-					propertyHolder, property, inferredData, entityBinder, mappings
-			);
-		}
-		else if ( joinColumns == null &&
-				( property.isAnnotationPresent( OneToMany.class )
-						|| property.isAnnotationPresent( CollectionOfElements.class ) //legacy Hibernate
-						|| property.isAnnotationPresent( ElementCollection.class )
-				) ) {
-			OneToMany oneToMany = property.getAnnotation( OneToMany.class );
-			String mappedBy = oneToMany != null ?
-					oneToMany.mappedBy() :
-					"";
-			joinColumns = Ejb3JoinColumn.buildJoinColumns(
-					(JoinColumn[]) null,
-					mappedBy, entityBinder.getSecondaryTables(),
-					propertyHolder, inferredData.getPropertyName(), mappings
-			);
-		}
-		else if ( joinColumns == null && property.isAnnotationPresent( org.hibernate.annotations.Any.class ) ) {
-			throw new AnnotationException( "@Any requires an explicit @JoinColumn(s): "
-					+ BinderHelper.getPath( propertyHolder, inferredData ) );
-		}
-		if ( columns == null && !property.isAnnotationPresent( ManyToMany.class ) ) {
-			//useful for collection of embedded elements
-			columns = Ejb3Column.buildColumnFromAnnotation(
-					null, null, nullability, propertyHolder, inferredData, entityBinder.getSecondaryTables(), mappings
-			);
-		}
-
-		if ( nullability == Nullability.FORCED_NOT_NULL ) {
-			//force columns to not null
-			for (Ejb3Column col : columns) {
-				col.forceNotNull();
-			}
-		}
-
 		final XClass returnedClass = inferredData.getClassOrElement();
 
 		//prepare PropertyBinder
@@ -1839,7 +1759,22 @@
 					|| property.isAnnotationPresent( EmbeddedId.class )
 					|| returnedClass.isAnnotationPresent( Embeddable.class );
 
+			//FIXME do the overrideColumnFromMapsIdProperty here and force the idclass type to look like an @embedded
+			//Overrides from @MapsId if needed
+			boolean isOverridden = false;
+			if ( isId || propertyHolder.isOrWithinEmbeddedId() ) {
+				Ejb3Column[] oldColumns = columns;
+				columns = columnsBuilder.overrideColumnFromMapsIdProperty(isId);
+				isOverridden = oldColumns != columns;
+			}
 			if ( isComponent ) {
+				String referencedEntityName = null;
+				if (isOverridden) {
+					final PropertyData mapsIdProperty = BinderHelper.getPropertyAnnotatedWithMapsId(
+							isId, propertyHolder, property.getName(), mappings
+					);
+					referencedEntityName = mapsIdProperty.getClassOrElementName();
+				}
 				AccessType propertyAccessor = entityBinder.getPropertyAccessor( property );
 				propertyBinder = bindComponent(
 						inferredData,
@@ -1850,7 +1785,9 @@
 						mappings,
 						isComponentEmbedded,
 						isId,
-						inheritanceStatePerClass
+						inheritanceStatePerClass,
+						referencedEntityName,
+						isOverridden ? (Ejb3JoinColumn[]) columns : null
 				);
 			}
 			else {
@@ -1870,16 +1807,6 @@
 					}
 				}
 
-				//Override from @MapsId if needed
-				if ( isId || propertyHolder.isOrWithinEmbeddedId() ) {
-					columns = overrideColumnFromMapsIdProperty(
-							isId ? "" : property.getName(), //@MapsId("") points to the id property
-							columns,
-							propertyHolder,
-							entityBinder,
-							mappings );
-				}
-
 				propertyBinder.setLazy( lazy );
 				propertyBinder.setColumns( columns );
 //				if ( isIdentifierMapper ) {
@@ -1968,69 +1895,6 @@
 		);
 	}
 
-	private static Ejb3JoinColumn[] buildDefaultJoinColumnsForXToOne(PropertyHolder propertyHolder, XProperty property, PropertyData inferredData, EntityBinder entityBinder, ExtendedMappings mappings) {
-		Ejb3JoinColumn[] joinColumns;
-		JoinTable joinTableAnn = propertyHolder.getJoinTable( property );
-		if ( joinTableAnn != null ) {
-			joinColumns = Ejb3JoinColumn.buildJoinColumns(
-					joinTableAnn.inverseJoinColumns(), null, entityBinder.getSecondaryTables(),
-					propertyHolder, inferredData.getPropertyName(), mappings
-			);
-			if ( StringHelper.isEmpty( joinTableAnn.name() ) ) {
-				throw new AnnotationException(
-						"JoinTable.name() on a @ToOne association has to be explicit: "
-								+ BinderHelper.getPath( propertyHolder, inferredData )
-				);
-			}
-		}
-		else {
-			OneToOne oneToOneAnn = property.getAnnotation( OneToOne.class );
-			String mappedBy = oneToOneAnn != null ?
-					oneToOneAnn.mappedBy() :
-					null;
-			joinColumns = Ejb3JoinColumn.buildJoinColumns(
-					( JoinColumn[]) null,
-					mappedBy, entityBinder.getSecondaryTables(),
-					propertyHolder, inferredData.getPropertyName(), mappings
-			);
-		}
-		return joinColumns;
-	}
-
-	private static Ejb3JoinColumn[] buildExplicitJoinColumns(PropertyHolder propertyHolder, XProperty property, PropertyData inferredData, EntityBinder entityBinder, ExtendedMappings mappings) {
-		//process @JoinColumn(s) before @Column(s) to handle collection of entities properly
-		Ejb3JoinColumn[] joinColumns = null;
-		{
-			JoinColumn[] anns = null;
-
-			if ( property.isAnnotationPresent( JoinColumn.class ) ) {
-				anns = new JoinColumn[] { property.getAnnotation( JoinColumn.class ) };
-			}
-			else if ( property.isAnnotationPresent( JoinColumns.class ) ) {
-				JoinColumns ann = property.getAnnotation( JoinColumns.class );
-				anns = ann.value();
-				int length = anns.length;
-				if ( length == 0 ) {
-					throw new AnnotationException( "Cannot bind an empty @JoinColumns" );
-				}
-			}
-			if ( anns != null ) {
-				joinColumns = Ejb3JoinColumn.buildJoinColumns(
-						anns, null, entityBinder.getSecondaryTables(),
-						propertyHolder, inferredData.getPropertyName(), mappings
-				);
-			}
-			else if ( property.isAnnotationPresent( JoinColumnsOrFormulas.class ) ) {
-				JoinColumnsOrFormulas ann = property.getAnnotation( JoinColumnsOrFormulas.class );
-				joinColumns = Ejb3JoinColumn.buildJoinColumnsOrFormulas(
-						ann, null, entityBinder.getSecondaryTables(),
-						propertyHolder, inferredData.getPropertyName(), mappings
-				);
-			}
-		}
-		return joinColumns;
-	}
-
 	//TODO move that to collection binder?
 	private static void bindJoinedTableAssociation(
 			XProperty property, ExtendedMappings mappings, EntityBinder entityBinder,
@@ -2107,14 +1971,27 @@
 			boolean isIdentifierMapper,
 			ExtendedMappings mappings,
 			boolean isComponentEmbedded,
-			boolean isId,
-			Map<XClass, InheritanceState> inheritanceStatePerClass
-	) {
-		Component comp = fillComponent(
-				propertyHolder, inferredData, propertyAccessor, !isId, entityBinder,
-				isComponentEmbedded, isIdentifierMapper,
-				false, mappings, inheritanceStatePerClass
-		);
+			boolean isId, //is a identifier
+			Map<XClass, InheritanceState> inheritanceStatePerClass,
+			String referencedEntityName, //is a component who is overridden by a @MapsId
+			Ejb3JoinColumn[] columns) {
+		Component comp;
+		if ( referencedEntityName != null ) {
+			comp = createComponent( propertyHolder, inferredData, isComponentEmbedded, isIdentifierMapper );
+			SecondPass sp = new CopyIdentifierComponentSecondPass(
+					comp,
+					referencedEntityName,
+					columns,
+					mappings);
+			mappings.addSecondPass( sp );
+		}
+		else {
+			comp = fillComponent(
+					propertyHolder, inferredData, propertyAccessor, !isId, entityBinder,
+					isComponentEmbedded, isIdentifierMapper,
+					false, mappings, inheritanceStatePerClass
+			);
+		}
 		if (isId) {
 			comp.setKey( true );
 			if ( propertyHolder.getPersistentClass().getIdentifier() != null ) {
@@ -2341,32 +2218,6 @@
 		}
 	}
 
-	private static Ejb3Column[] overrideColumnFromMapsIdProperty(String propertyPath,
-																 Ejb3Column[] columns,
-																 PropertyHolder propertyHolder,
-																 EntityBinder entityBinder,
-																 ExtendedMappings mappings) {
-		Ejb3Column[] result = columns;
-		final XClass persistentXClass;
-		try {
-			 persistentXClass = mappings.getReflectionManager()
-					.classForName( propertyHolder.getPersistentClass().getClassName(), AnnotationBinder.class );
-		}
-		catch ( ClassNotFoundException e ) {
-			throw new AssertionFailure( "PersistentClass name cannot be converted into a Class", e);
-		}
-		final PropertyData annotatedWithMapsId = mappings.getPropertyAnnotatedWithMapsId( persistentXClass, propertyPath );
-		if ( annotatedWithMapsId != null ) {
-			result = buildExplicitJoinColumns( propertyHolder, annotatedWithMapsId.getProperty(), annotatedWithMapsId, entityBinder, mappings );
-			if (result == null) {
-				result = buildDefaultJoinColumnsForXToOne( propertyHolder, annotatedWithMapsId.getProperty(), annotatedWithMapsId, entityBinder, mappings );
-				throw new UnsupportedOperationException( "Implicit @JoinColumn is not supported on @MapsId properties: "
-						+ annotatedWithMapsId.getDeclaringClass() + " " + annotatedWithMapsId.getPropertyName() );
-			}
-		}
-		return result;
-	}
-
 	private static void setupComponentTuplizer(XProperty property, Component component) {
 		if ( property == null ) return;
 		if ( property.isAnnotationPresent( Tuplizers.class ) ) {
@@ -2459,9 +2310,7 @@
 		propertyBinder.setCascade( cascadeStrategy );
 		propertyBinder.setProperty( property );
 		propertyBinder.setXToMany( true );
-		Property prop = propertyBinder.makePropertyAndBind();
-		//composite FK columns are in the same table so its OK
-		//propertyHolder.addProperty( prop, columns, inferredData.getDeclaringClass() );
+		propertyBinder.makePropertyAndBind();
 	}
 
 	protected static void defineFetchingStrategy(ToOne toOne, XProperty property) {
@@ -2768,14 +2617,17 @@
 	 * @param orderedClasses Order list of all annotated entities and their mapped superclasses
 	 * @return A map of {@code InheritanceState}s keyed against their {@code XClass}.
 	 */
-	public static Map<XClass, InheritanceState> buildInheritanceStates(List<XClass> orderedClasses) {
+	public static Map<XClass, InheritanceState> buildInheritanceStates(
+			List<XClass> orderedClasses,
+			ExtendedMappings mappings) {
+		ReflectionManager reflectionManager = mappings.getReflectionManager();
 		Map<XClass, InheritanceState> inheritanceStatePerClass = new HashMap<XClass, InheritanceState>(
 				orderedClasses.size()
 		);
 		for (XClass clazz : orderedClasses) {
 			InheritanceState superclassState = InheritanceState.getSuperclassInheritanceState(
 					clazz, inheritanceStatePerClass );
-			InheritanceState state = new InheritanceState( clazz );
+			InheritanceState state = new InheritanceState( clazz, inheritanceStatePerClass, mappings );
 			if ( superclassState != null ) {
 				//the classes are ordered thus preventing an NPE
 				//FIXME if an entity has subclasses annotated @MappedSperclass wo sub @Entity this is wrong
@@ -2827,4 +2679,5 @@
 		}
 		return false;
 	}
+
 }

Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java	2010-01-26 12:19:38 UTC (rev 18624)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java	2010-01-26 12:57:01 UTC (rev 18625)
@@ -359,35 +359,11 @@
 		caches.clear();
 		try {
 			inSecondPass = true;
-			Iterator iter = secondPasses.iterator();
-			while ( iter.hasNext() ) {
-				SecondPass sp = ( SecondPass ) iter.next();
-				//do the second pass of simple value types first and remove them
-				if ( sp instanceof SetSimpleValueTypeSecondPass ) {
-					sp.doSecondPass( classes );
-					iter.remove();
-				}
-			}
+			processSecondPassesOfType( SetSimpleValueTypeSecondPass.class );
+			processSecondPassesOfType( CopyIdentifierComponentSecondPass.class );
 			processFkSecondPassInOrder();
-			iter = secondPasses.iterator();
-			while ( iter.hasNext() ) {
-				SecondPass sp = ( SecondPass ) iter.next();
-				//do the second pass of fk before the others and remove them
-				if ( sp instanceof CreateKeySecondPass ) {
-					sp.doSecondPass( classes );
-					iter.remove();
-				}
-			}
-
-			iter = secondPasses.iterator();
-			while ( iter.hasNext() ) {
-				SecondPass sp = ( SecondPass ) iter.next();
-				//do the SecondaryTable second pass before any association because associations can be built on joins
-				if ( sp instanceof SecondaryTableSecondPass ) {
-					sp.doSecondPass( classes );
-					iter.remove();
-				}
-			}
+			processSecondPassesOfType( CreateKeySecondPass.class );
+			processSecondPassesOfType( SecondaryTableSecondPass.class );
 			super.secondPassCompile();
 			inSecondPass = false;
 		}
@@ -413,6 +389,18 @@
 		applyConstraintsToDDL();
 	}
 
+	private void processSecondPassesOfType(Class<? extends SecondPass> type) {
+		Iterator iter = secondPasses.iterator();
+		while ( iter.hasNext() ) {
+			SecondPass sp = ( SecondPass ) iter.next();
+			//do the second pass of simple value types first and remove them
+			if ( type.isInstance( sp ) ) {
+				sp.doSecondPass( classes );
+				iter.remove();
+			}
+		}
+	}
+
 	private void applyConstraintsToDDL() {
 		boolean applyOnDdl = getProperties().getProperty(
 				"hibernate.validator.apply_to_ddl",
@@ -623,11 +611,12 @@
 			log.debug( "Process annotated classes" );
 			//bind classes in the correct order calculating some inheritance state
 			List<XClass> orderedClasses = orderAndFillHierarchy( annotatedClasses );
+			ExtendedMappings mappings = createExtendedMappings();
 			Map<XClass, InheritanceState> inheritanceStatePerClass = AnnotationBinder.buildInheritanceStates(
-					orderedClasses
+					orderedClasses, mappings
 			);
-			ExtendedMappings mappings = createExtendedMappings();
 
+
 			for ( XClass clazz : orderedClasses ) {
 				//todo use the same extended mapping
 				AnnotationBinder.bindClass( clazz, inheritanceStatePerClass, mappings );

Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/BinderHelper.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/BinderHelper.java	2010-01-26 12:19:38 UTC (rev 18624)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/BinderHelper.java	2010-01-26 12:57:01 UTC (rev 18625)
@@ -652,4 +652,17 @@
 		return StringHelper.qualify( holder.getPath(), property.getPropertyName() );
 	}
 
+	static PropertyData getPropertyAnnotatedWithMapsId(boolean isId, PropertyHolder propertyHolder, String propertyName, ExtendedMappings mappings) {
+		final XClass persistentXClass;
+		try {
+			 persistentXClass = mappings.getReflectionManager()
+					.classForName( propertyHolder.getPersistentClass().getClassName(), AnnotationBinder.class );
+		}
+		catch ( ClassNotFoundException e ) {
+			throw new AssertionFailure( "PersistentClass name cannot be converted into a Class", e);
+		}
+		String propertyPath = isId ? "" : propertyName;
+		final PropertyData annotatedWithMapsId = mappings.getPropertyAnnotatedWithMapsId( persistentXClass, propertyPath );
+		return annotatedWithMapsId;
+	}
 }

Added: core/trunk/annotations/src/main/java/org/hibernate/cfg/ColumnsBuilder.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/ColumnsBuilder.java	                        (rev 0)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/ColumnsBuilder.java	2010-01-26 12:57:01 UTC (rev 18625)
@@ -0,0 +1,199 @@
+package org.hibernate.cfg;
+
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinColumns;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+
+import org.hibernate.AnnotationException;
+import org.hibernate.AssertionFailure;
+import org.hibernate.annotations.CollectionOfElements;
+import org.hibernate.annotations.Columns;
+import org.hibernate.annotations.Formula;
+import org.hibernate.annotations.JoinColumnsOrFormulas;
+import org.hibernate.annotations.common.reflection.XClass;
+import org.hibernate.annotations.common.reflection.XProperty;
+import org.hibernate.cfg.annotations.EntityBinder;
+import org.hibernate.cfg.annotations.Nullability;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Do the initial discovery of columns metadata and apply defaults.
+ * Also hosts some convinient methods related to column processing
+ *
+ * @author Emmanuel Bernard
+ */
+class ColumnsBuilder {
+	private PropertyHolder propertyHolder;
+	private Nullability nullability;
+	private XProperty property;
+	private PropertyData inferredData;
+	private EntityBinder entityBinder;
+	private ExtendedMappings mappings;
+	private Ejb3Column[] columns;
+	private Ejb3JoinColumn[] joinColumns;
+
+	public ColumnsBuilder(PropertyHolder propertyHolder, Nullability nullability, XProperty property, PropertyData inferredData, EntityBinder entityBinder, ExtendedMappings mappings) {
+		this.propertyHolder = propertyHolder;
+		this.nullability = nullability;
+		this.property = property;
+		this.inferredData = inferredData;
+		this.entityBinder = entityBinder;
+		this.mappings = mappings;
+	}
+
+	public Ejb3Column[] getColumns() {
+		return columns;
+	}
+
+	public Ejb3JoinColumn[] getJoinColumns() {
+		return joinColumns;
+	}
+
+	public ColumnsBuilder extractMetadata() {
+		columns = null;
+		joinColumns = buildExplicitJoinColumns(property, inferredData);
+
+
+		if ( property.isAnnotationPresent( Column.class ) || property.isAnnotationPresent( Formula.class ) ) {
+			Column ann = property.getAnnotation( Column.class );
+			Formula formulaAnn = property.getAnnotation( Formula.class );
+			columns = Ejb3Column.buildColumnFromAnnotation(
+					new Column[] { ann }, formulaAnn, nullability, propertyHolder, inferredData,
+					entityBinder.getSecondaryTables(), mappings
+			);
+		}
+		else if ( property.isAnnotationPresent( Columns.class ) ) {
+			Columns anns = property.getAnnotation( Columns.class );
+			columns = Ejb3Column.buildColumnFromAnnotation(
+					anns.columns(), null,
+					nullability, propertyHolder, inferredData, entityBinder.getSecondaryTables(),
+					mappings
+			);
+		}
+
+		//set default values if needed
+		if ( joinColumns == null &&
+				( property.isAnnotationPresent( ManyToOne.class )
+						|| property.isAnnotationPresent( OneToOne.class ) )
+				) {
+			joinColumns = buildDefaultJoinColumnsForXToOne(property, inferredData);
+		}
+		else if ( joinColumns == null &&
+				( property.isAnnotationPresent( OneToMany.class )
+						|| property.isAnnotationPresent( CollectionOfElements.class ) //legacy Hibernate
+						|| property.isAnnotationPresent( ElementCollection.class )
+				) ) {
+			OneToMany oneToMany = property.getAnnotation( OneToMany.class );
+			String mappedBy = oneToMany != null ?
+					oneToMany.mappedBy() :
+					"";
+			joinColumns = Ejb3JoinColumn.buildJoinColumns(
+					null,
+					mappedBy, entityBinder.getSecondaryTables(),
+					propertyHolder, inferredData.getPropertyName(), mappings
+			);
+		}
+		else if ( joinColumns == null && property.isAnnotationPresent( org.hibernate.annotations.Any.class ) ) {
+			throw new AnnotationException( "@Any requires an explicit @JoinColumn(s): "
+					+ BinderHelper.getPath( propertyHolder, inferredData ) );
+		}
+		if ( columns == null && !property.isAnnotationPresent( ManyToMany.class ) ) {
+			//useful for collection of embedded elements
+			columns = Ejb3Column.buildColumnFromAnnotation(
+					null, null,
+					nullability, propertyHolder, inferredData, entityBinder.getSecondaryTables(), mappings
+			);
+		}
+
+		if ( nullability == Nullability.FORCED_NOT_NULL ) {
+			//force columns to not null
+			for (Ejb3Column col : columns ) {
+				col.forceNotNull();
+			}
+		}
+		return this;
+	}
+
+	Ejb3JoinColumn[] buildDefaultJoinColumnsForXToOne(XProperty property, PropertyData inferredData) {
+		Ejb3JoinColumn[] joinColumns;
+		JoinTable joinTableAnn = propertyHolder.getJoinTable( property );
+		if ( joinTableAnn != null ) {
+			joinColumns = Ejb3JoinColumn.buildJoinColumns(
+					joinTableAnn.inverseJoinColumns(), null, entityBinder.getSecondaryTables(),
+					propertyHolder, inferredData.getPropertyName(), mappings
+			);
+			if ( StringHelper.isEmpty( joinTableAnn.name() ) ) {
+				throw new AnnotationException(
+						"JoinTable.name() on a @ToOne association has to be explicit: "
+								+ BinderHelper.getPath( propertyHolder, inferredData )
+				);
+			}
+		}
+		else {
+			OneToOne oneToOneAnn = property.getAnnotation( OneToOne.class );
+			String mappedBy = oneToOneAnn != null ?
+					oneToOneAnn.mappedBy() :
+					null;
+			joinColumns = Ejb3JoinColumn.buildJoinColumns(
+					null,
+					mappedBy, entityBinder.getSecondaryTables(),
+					propertyHolder, inferredData.getPropertyName(), mappings
+			);
+		}
+		return joinColumns;
+	}
+
+	Ejb3JoinColumn[] buildExplicitJoinColumns(XProperty property, PropertyData inferredData) {
+		//process @JoinColumn(s) before @Column(s) to handle collection of entities properly
+		Ejb3JoinColumn[] joinColumns = null;
+		{
+			JoinColumn[] anns = null;
+
+			if ( property.isAnnotationPresent( JoinColumn.class ) ) {
+				anns = new JoinColumn[] { property.getAnnotation( JoinColumn.class ) };
+			}
+			else if ( property.isAnnotationPresent( JoinColumns.class ) ) {
+				JoinColumns ann = property.getAnnotation( JoinColumns.class );
+				anns = ann.value();
+				int length = anns.length;
+				if ( length == 0 ) {
+					throw new AnnotationException( "Cannot bind an empty @JoinColumns" );
+				}
+			}
+			if ( anns != null ) {
+				joinColumns = Ejb3JoinColumn.buildJoinColumns(
+						anns, null, entityBinder.getSecondaryTables(),
+						propertyHolder, inferredData.getPropertyName(), mappings
+				);
+			}
+			else if ( property.isAnnotationPresent( JoinColumnsOrFormulas.class ) ) {
+				JoinColumnsOrFormulas ann = property.getAnnotation( JoinColumnsOrFormulas.class );
+				joinColumns = Ejb3JoinColumn.buildJoinColumnsOrFormulas(
+						ann, null, entityBinder.getSecondaryTables(),
+						propertyHolder, inferredData.getPropertyName(), mappings
+				);
+			}
+		}
+		return joinColumns;
+	}
+
+	Ejb3Column[] overrideColumnFromMapsIdProperty(boolean isId) {
+		Ejb3Column[] result = columns;
+		final PropertyData annotatedWithMapsId = BinderHelper.getPropertyAnnotatedWithMapsId( isId, propertyHolder, property.getName(), mappings );
+		if ( annotatedWithMapsId != null ) {
+			result = buildExplicitJoinColumns( annotatedWithMapsId.getProperty(), annotatedWithMapsId );
+			if (result == null) {
+				//result = buildDefaultJoinColumnsForXToOne( annotatedWithMapsId.getProperty(), annotatedWithMapsId);
+				throw new UnsupportedOperationException( "Implicit @JoinColumn is not supported on @MapsId properties: "
+						+ annotatedWithMapsId.getDeclaringClass() + " " + annotatedWithMapsId.getPropertyName() );
+			}
+		}
+		return result;
+	}
+}

Added: core/trunk/annotations/src/main/java/org/hibernate/cfg/CopyIdentifierComponentSecondPass.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/CopyIdentifierComponentSecondPass.java	                        (rev 0)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/CopyIdentifierComponentSecondPass.java	2010-01-26 12:57:01 UTC (rev 18625)
@@ -0,0 +1,118 @@
+package org.hibernate.cfg;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.hibernate.AnnotationException;
+import org.hibernate.AssertionFailure;
+import org.hibernate.MappingException;
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.mapping.SimpleValue;
+import org.hibernate.mapping.Value;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class CopyIdentifierComponentSecondPass implements SecondPass {
+	private final String referencedEntityName;
+	private final Component component;
+	private final ExtendedMappings mappings;
+	private final Ejb3JoinColumn[] joinColumns;
+
+	public CopyIdentifierComponentSecondPass(
+			Component comp, String referencedEntityName, Ejb3JoinColumn[] joinColumns, ExtendedMappings mappings) {
+		this.component = comp;
+		this.referencedEntityName = referencedEntityName;
+		this.mappings = mappings;
+		this.joinColumns = joinColumns;
+	}
+
+	//FIXME better error names
+	public void doSecondPass(Map persistentClasses) throws MappingException {
+		PersistentClass referencedPersistentClass = (PersistentClass) persistentClasses.get( referencedEntityName );
+		if ( referencedPersistentClass == null ) {
+			throw new AnnotationException(
+					"Unknown entity name: " + referencedEntityName
+			);
+		};
+		if ( ! ( referencedPersistentClass.getIdentifier() instanceof Component ) ) {
+			throw new AssertionFailure( "Unexpected identifier type on the referenced entity when mapping a @MapsId: "
+					+ referencedEntityName);
+		}
+		Component referencedComponent = (Component) referencedPersistentClass.getIdentifier();
+		Iterator<Property> properties = referencedComponent.getPropertyIterator();
+
+		//prepare column name structure
+		boolean isExplicitReference = true;
+		Map<String, Ejb3JoinColumn> columnByReferencedName = new HashMap<String, Ejb3JoinColumn>(joinColumns.length);
+		for (Ejb3JoinColumn joinColumn : joinColumns) {
+			final String referencedColumnName = joinColumn.getReferencedColumn();
+			if ( BinderHelper.isDefault( referencedColumnName ) ) {
+				break;
+			}
+			columnByReferencedName.put( referencedColumnName, joinColumn );
+		}
+		//try default column orientation
+		int index = 0;
+		if ( columnByReferencedName.isEmpty() ) {
+			isExplicitReference = false;
+			for (Ejb3JoinColumn joinColumn : joinColumns) {
+				columnByReferencedName.put( "" + index, joinColumn );
+				index++;
+			}
+			index = 0;
+		}
+
+		while ( properties.hasNext() ) {
+			Property referencedProperty = properties.next();
+			if ( referencedProperty.isComposite() ) {
+				throw new AssertionFailure( "Unexpected nested component on the referenced entity when mapping a @MapsId: "
+						+ referencedEntityName);
+			}
+			else {
+				Property property = new Property();
+				property.setName( referencedProperty.getName() );
+				property.setNodeName( referencedProperty.getNodeName() );
+				//FIXME set optional?
+				//property.setOptional( property.isOptional() );
+				property.setPersistentClass( component.getOwner() );
+				property.setPropertyAccessorName( referencedProperty.getPropertyAccessorName() );
+				SimpleValue value = new SimpleValue( component.getTable() );
+				property.setValue( value );
+				final SimpleValue referencedValue = (SimpleValue) referencedProperty.getValue();
+				value.setTypeName( referencedValue.getTypeName() );
+				value.setTypeParameters( referencedValue.getTypeParameters() );
+				final Iterator<Column> columns = referencedValue.getColumnIterator();
+				//FIXME take care of Formula
+				while ( columns.hasNext() ) {
+					Column column = columns.next();
+					final Ejb3JoinColumn joinColumn;
+					String logicalColumnName = null;
+					if ( isExplicitReference ) {
+						final String columnName = column.getName();
+						logicalColumnName = mappings.getLogicalColumnName( columnName, referencedPersistentClass.getTable() );
+						joinColumn = columnByReferencedName.get( logicalColumnName );
+					}
+					else {
+						joinColumn = columnByReferencedName.get( "" + index );
+						index++;
+					}
+					if (joinColumn == null) {
+						throw new AnnotationException(
+								isExplicitReference ?
+										"Unable to find column reference in the @MapsId mapping: " + logicalColumnName :
+										"Implicit column reference in the @MapsId mapping fails, try to use explicit referenceColumnNames: " + referencedEntityName
+						);
+					}
+					value.addColumn( new Column( joinColumn.getName() ) );
+					column.setValue( value );
+				}
+				component.addProperty( property );
+			}
+		}
+	}
+}

Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/InheritanceState.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/InheritanceState.java	2010-01-26 12:19:38 UTC (rev 18624)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/InheritanceState.java	2010-01-26 12:57:01 UTC (rev 18625)
@@ -23,13 +23,24 @@
  */
 package org.hibernate.cfg;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
+import javax.persistence.EmbeddedId;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
 import javax.persistence.Inheritance;
 import javax.persistence.InheritanceType;
 import javax.persistence.MappedSuperclass;
 
+import org.hibernate.AnnotationException;
+import org.hibernate.annotations.common.reflection.ReflectionManager;
 import org.hibernate.annotations.common.reflection.XAnnotatedElement;
 import org.hibernate.annotations.common.reflection.XClass;
+import org.hibernate.annotations.common.reflection.XProperty;
+import org.hibernate.cfg.annotations.EntityBinder;
+import org.hibernate.mapping.PersistentClass;
 
 /**
  * Some extra data to the inheritance position of a class.
@@ -37,12 +48,8 @@
  * @author Emmanuel Bernard
  */
 public class InheritanceState {
-	public InheritanceState(XClass clazz) {
-		this.setClazz( clazz );
-		extractInheritanceType();
-	}
-
 	private XClass clazz;
+	private XClass identifierType;
 
 	/**
 	 * Has sibling (either mappedsuperclass entity)
@@ -55,7 +62,22 @@
 	private boolean hasParents = false;
 	private InheritanceType type;
 	private boolean isEmbeddableSuperclass = false;
+	private Map<XClass, InheritanceState> inheritanceStatePerClass;
+	private List<XClass> classesToProcessForMappedSuperclass = new ArrayList<XClass>();
+	private ExtendedMappings mappings;
+	private AccessType accessType;
+	private ElementsToProcess elementsToProcess;
 
+	public InheritanceState(XClass clazz,
+							Map<XClass, InheritanceState> inheritanceStatePerClass,
+							ExtendedMappings mappings) {
+		this.setClazz( clazz );
+		this.mappings = mappings;
+		this.inheritanceStatePerClass = inheritanceStatePerClass;
+		this.identifierType = mappings.getReflectionManager().toXClass( void.class ); //not initialized
+		extractInheritanceType();
+	}
+
 	private void extractInheritanceType() {
 		XAnnotatedElement element = getClazz();
 		Inheritance inhAnn = element.getAnnotation( Inheritance.class );
@@ -144,4 +166,170 @@
 	public void setEmbeddableSuperclass(boolean embeddableSuperclass) {
 		isEmbeddableSuperclass = embeddableSuperclass;
 	}
+
+	public XClass getIdentifierTypeIfComponent() {
+		final ReflectionManager reflectionManager = mappings.getReflectionManager();
+		if ( reflectionManager.equals( identifierType, void.class ) ) {
+			IdClass idClass = clazz.getAnnotation( IdClass.class );
+			if (idClass != null) {
+				identifierType =  reflectionManager.toXClass( idClass.value() );
+			}
+			else {
+				//find @EmbeddedId
+				getElementsToProcess();
+			}
+		}
+		return identifierType;
+	}
+
+	void postProcess(PersistentClass persistenceClass, EntityBinder entityBinder) {
+		//make sure we run elements to process
+		getElementsToProcess();
+		addMappedSuperClassInMetadata(persistenceClass);
+		entityBinder.setPropertyAccessType( accessType );
+	}
+
+	public XClass getClassWithIdClass(boolean evenIfSubclass) {
+		if ( !evenIfSubclass && hasParents() ) {
+			return null;
+		}
+		if ( clazz.isAnnotationPresent( IdClass.class ) ) {
+			return clazz;
+		}
+		else {
+			InheritanceState state = InheritanceState.getSuperclassInheritanceState( clazz, inheritanceStatePerClass );
+			if (state != null){
+				return state.getClassWithIdClass(true);
+			}
+			else {
+				return null;
+			}
+
+		}
+	}
+
+	/*
+	 * Get the annotated elements, guessing the access type from @Id or @EmbeddedId presence.
+	 * Change EntityBinder by side effect
+	 */
+	public ElementsToProcess getElementsToProcess() {
+		if (elementsToProcess == null) {
+			InheritanceState inheritanceState = inheritanceStatePerClass.get( clazz );
+			assert !inheritanceState.isEmbeddableSuperclass();
+
+
+			getMappedSuperclassesTillNextEntityOrdered();
+
+			accessType = determineDefaultAccessType( );
+
+			List<PropertyData> elements = new ArrayList<PropertyData>();
+			int deep = classesToProcessForMappedSuperclass.size();
+			int idPropertyCount = 0;
+
+			for ( int index = 0; index < deep; index++ ) {
+				PropertyContainer propertyContainer = new PropertyContainer( classesToProcessForMappedSuperclass.get( index ), clazz );
+				int currentIdPropertyCount = AnnotationBinder.addElementsOfClass( elements, accessType, propertyContainer, mappings );
+				idPropertyCount +=  currentIdPropertyCount;
+			}
+
+			if ( idPropertyCount == 0 && !inheritanceState.hasParents() ) {
+				throw new AnnotationException( "No identifier specified for entity: " + clazz.getName() );
+			}
+			elementsToProcess = new ElementsToProcess( elements, idPropertyCount);
+		}
+		return elementsToProcess;
+	}
+
+	private AccessType determineDefaultAccessType() {
+		XClass xclass = clazz;
+		while ( xclass != null && !Object.class.getName().equals( xclass.getName() ) ) {
+			if ( xclass.isAnnotationPresent( Entity.class ) || xclass.isAnnotationPresent( MappedSuperclass.class ) ) {
+				for ( XProperty prop : xclass.getDeclaredProperties( AccessType.PROPERTY.getType() ) ) {
+					final boolean isEmbeddedId = prop.isAnnotationPresent( EmbeddedId.class );
+					if ( prop.isAnnotationPresent( Id.class ) || isEmbeddedId ) {
+						if (isEmbeddedId) {
+							identifierType = prop.getClassOrElementClass();
+						}
+						return AccessType.PROPERTY;
+					}
+				}
+				for ( XProperty prop : xclass.getDeclaredProperties( AccessType.FIELD.getType() ) ) {
+					final boolean isEmbeddedId = prop.isAnnotationPresent( EmbeddedId.class );
+					if ( prop.isAnnotationPresent( Id.class ) || isEmbeddedId ) {
+						if ( isEmbeddedId ) {
+							identifierType = prop.getClassOrElementClass();
+						}
+						return AccessType.FIELD;
+					}
+				}
+			}
+			xclass = xclass.getSuperclass();
+		}
+		throw new AnnotationException( "No identifier specified for entity: " + clazz.getName() );
+	}
+
+	private void getMappedSuperclassesTillNextEntityOrdered() {
+
+		//ordered to allow proper messages on properties subclassing
+		XClass currentClassInHierarchy = clazz;
+		InheritanceState superclassState;
+		do {
+			classesToProcessForMappedSuperclass.add( 0, currentClassInHierarchy );
+			XClass superClass = currentClassInHierarchy;
+			do {
+				superClass = superClass.getSuperclass();
+				superclassState = inheritanceStatePerClass.get( superClass );
+			}
+			while ( superClass != null
+					&& !mappings.getReflectionManager().equals( superClass, Object.class ) && superclassState == null );
+
+			currentClassInHierarchy = superClass;
+		}
+		while ( superclassState != null && superclassState.isEmbeddableSuperclass() );
+	}
+
+	private void addMappedSuperClassInMetadata(PersistentClass persistentClass) {
+		//add @MappedSuperclass in the metadata
+		// classes from 0 to n-1 are @MappedSuperclass and should be linked
+		org.hibernate.mapping.MappedSuperclass mappedSuperclass = null;
+		final InheritanceState superEntityState =
+				InheritanceState.getInheritanceStateOfSuperEntity( clazz, inheritanceStatePerClass );
+		PersistentClass superEntity =
+				superEntityState != null ?
+						mappings.getClass( superEntityState.getClazz().getName() ) :
+						null;
+		final int lastMappedSuperclass = classesToProcessForMappedSuperclass.size() - 1;
+		for ( int index = 0 ; index < lastMappedSuperclass ; index++ ) {
+			org.hibernate.mapping.MappedSuperclass parentSuperclass = mappedSuperclass;
+			final Class<?> type = mappings.getReflectionManager().toClass( classesToProcessForMappedSuperclass.get( index ) );
+			//add MAppedSuperclass if not already there
+			mappedSuperclass = mappings.getMappedSuperclass( type );
+			if (mappedSuperclass == null) {
+				mappedSuperclass = new org.hibernate.mapping.MappedSuperclass(parentSuperclass, superEntity );
+				mappedSuperclass.setMappedClass( type );
+				mappings.addMappedSuperclass( type, mappedSuperclass );
+			}
+		}
+		if (mappedSuperclass != null) {
+			persistentClass.setSuperMappedSuperclass(mappedSuperclass);
+		}
+	}
+
+	static final class ElementsToProcess {
+		private final List<PropertyData> properties;
+		private final int idPropertyCount;
+
+		public List<PropertyData> getElements() {
+			return properties;
+		}
+
+		public int getIdPropertyCount() {
+			return idPropertyCount;
+		}
+
+		private ElementsToProcess(List<PropertyData> properties, int idPropertyCount) {
+			this.properties = properties;
+			this.idPropertyCount = idPropertyCount;
+		}
+	}
 }

Modified: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/DerivedIdentitySimpleParentEmbeddedIdDepTest.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/DerivedIdentitySimpleParentEmbeddedIdDepTest.java	2010-01-26 12:19:38 UTC (rev 18624)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/b/DerivedIdentitySimpleParentEmbeddedIdDepTest.java	2010-01-26 12:57:01 UTC (rev 18625)
@@ -7,7 +7,8 @@
 /**
  * @author Emmanuel Bernard
  */
-public class DerivedIdentitySimpleParentEmbeddedIdDepTest extends TestCase {
+public class
+		DerivedIdentitySimpleParentEmbeddedIdDepTest extends TestCase {
 
 	public void testManyToOne() throws Exception {
 		assertTrue( SchemaUtil.isColumnPresent( "Dependent", "FK", getCfg() ) );

Modified: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/c/Dependent.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/c/Dependent.java	2010-01-26 12:19:38 UTC (rev 18624)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e1/c/Dependent.java	2010-01-26 12:57:01 UTC (rev 18625)
@@ -17,7 +17,6 @@
 
 
 	@Id
-	//@JoinColumn(name = "FK")
 	// id attribute mapped by join column default
 	@ManyToOne
 	Employee emp;

Modified: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/DependentId.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/DependentId.java	2010-01-26 12:19:38 UTC (rev 18624)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/DependentId.java	2010-01-26 12:57:01 UTC (rev 18625)
@@ -2,6 +2,7 @@
 
 import java.io.Serializable;
 import javax.persistence.Embeddable;
+import javax.persistence.Embedded;
 
 /**
  * @author Emmanuel Bernard

Added: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/DerivedIdentityIdClassParentEmbeddedIdDepTest.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/DerivedIdentityIdClassParentEmbeddedIdDepTest.java	                        (rev 0)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/DerivedIdentityIdClassParentEmbeddedIdDepTest.java	2010-01-26 12:57:01 UTC (rev 18625)
@@ -0,0 +1,49 @@
+package org.hibernate.test.annotations.derivedidentities.e2.b;
+
+import org.hibernate.Session;
+import org.hibernate.test.annotations.TestCase;
+import org.hibernate.test.util.SchemaUtil;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class DerivedIdentityIdClassParentEmbeddedIdDepTest extends TestCase {
+
+	public void testManyToOne() throws Exception {
+		assertTrue( SchemaUtil.isColumnPresent( "Dependent", "FK1", getCfg() ) );
+		assertTrue( SchemaUtil.isColumnPresent( "Dependent", "FK2", getCfg() ) );
+		assertTrue( SchemaUtil.isColumnPresent( "Dependent", "name", getCfg() ) );
+		assertTrue( ! SchemaUtil.isColumnPresent( "Dependent", "firstName", getCfg() ) );
+		assertTrue( ! SchemaUtil.isColumnPresent( "Dependent", "lastName", getCfg() ) );
+		Employee e = new Employee();
+		e.firstName = "Emmanuel";
+		e.lastName = "Bernard";
+		Session s = openSession(  );
+		s.getTransaction().begin();
+		s.persist( e );
+		Dependent d = new Dependent();
+		d.emp = e;
+		d.id = new DependentId();
+		d.id.name = "Doggy";
+		d.id.empPK = new EmployeeId();
+		d.id.empPK.firstName = e.firstName; //FIXME not needed when foreign is enabled
+		d.id.empPK.lastName = e.lastName; //FIXME not needed when foreign is enabled
+		s.persist( d );
+		s.flush();
+		s.clear();
+		d = (Dependent) s.get( Dependent.class, d.id );
+		assertNotNull( d.emp );
+		assertEquals( e.firstName, d.emp.firstName );
+		s.getTransaction().rollback();
+		s.close();
+	}
+
+
+	@Override
+	protected Class<?>[] getAnnotatedClasses() {
+		return new Class<?>[] {
+				Dependent.class,
+				Employee.class
+		};
+	}
+}

Modified: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/EmployeeId.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/EmployeeId.java	2010-01-26 12:19:38 UTC (rev 18624)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/derivedidentities/e2/b/EmployeeId.java	2010-01-26 12:57:01 UTC (rev 18625)
@@ -1,9 +1,13 @@
 package org.hibernate.test.annotations.derivedidentities.e2.b;
 
+import java.io.Serializable;
+import javax.persistence.Embeddable;
+
 /**
  * @author Emmanuel Bernard
  */
-public class EmployeeId {
+ at Embeddable
+public class EmployeeId implements Serializable {
 	String firstName;
 	String lastName;
 }
\ No newline at end of file



More information about the hibernate-commits mailing list