[hibernate-commits] Hibernate SVN: r19563 - in core/trunk/core/src/main/java/org/hibernate: loader and 3 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Thu May 20 01:37:43 EDT 2010


Author: steve.ebersole at jboss.com
Date: 2010-05-20 01:37:43 -0400 (Thu, 20 May 2010)
New Revision: 19563

Added:
   core/trunk/core/src/main/java/org/hibernate/loader/PropertyPath.java
Modified:
   core/trunk/core/src/main/java/org/hibernate/impl/SessionImpl.java
   core/trunk/core/src/main/java/org/hibernate/loader/AbstractEntityJoinWalker.java
   core/trunk/core/src/main/java/org/hibernate/loader/JoinWalker.java
   core/trunk/core/src/main/java/org/hibernate/loader/Loader.java
   core/trunk/core/src/main/java/org/hibernate/loader/OuterJoinableAssociation.java
   core/trunk/core/src/main/java/org/hibernate/loader/collection/BasicCollectionJoinWalker.java
   core/trunk/core/src/main/java/org/hibernate/loader/collection/OneToManyJoinWalker.java
   core/trunk/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java
   core/trunk/core/src/main/java/org/hibernate/loader/entity/EntityJoinWalker.java
   core/trunk/core/src/main/java/org/hibernate/loader/entity/EntityLoader.java
Log:
HHH-2277 - bidirectional <key-many-to-one> both lazy=false fetch=join lead to infinite loop


Modified: core/trunk/core/src/main/java/org/hibernate/impl/SessionImpl.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/impl/SessionImpl.java	2010-05-19 18:29:45 UTC (rev 19562)
+++ core/trunk/core/src/main/java/org/hibernate/impl/SessionImpl.java	2010-05-20 05:37:43 UTC (rev 19563)
@@ -1022,9 +1022,11 @@
 
 	public Object internalLoad(String entityName, Serializable id, boolean eager, boolean nullable) throws HibernateException {
 		// todo : remove
-		LoadEventListener.LoadType type = nullable ? 
-				LoadEventListener.INTERNAL_LOAD_NULLABLE : 
-				eager ? LoadEventListener.INTERNAL_LOAD_EAGER : LoadEventListener.INTERNAL_LOAD_LAZY;
+		LoadEventListener.LoadType type = nullable
+				? LoadEventListener.INTERNAL_LOAD_NULLABLE
+				: eager
+						? LoadEventListener.INTERNAL_LOAD_EAGER
+						: LoadEventListener.INTERNAL_LOAD_LAZY;
 		LoadEvent event = new LoadEvent(id, entityName, true, this);
 		fireLoad(event, type);
 		if ( !nullable ) {

Modified: core/trunk/core/src/main/java/org/hibernate/loader/AbstractEntityJoinWalker.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/AbstractEntityJoinWalker.java	2010-05-19 18:29:45 UTC (rev 19562)
+++ core/trunk/core/src/main/java/org/hibernate/loader/AbstractEntityJoinWalker.java	2010-05-20 05:37:43 UTC (rev 19563)
@@ -29,7 +29,6 @@
 import java.util.Iterator;
 
 import org.hibernate.FetchMode;
-import org.hibernate.LockMode;
 import org.hibernate.MappingException;
 import org.hibernate.LockOptions;
 import org.hibernate.engine.CascadeStyle;
@@ -42,7 +41,6 @@
 import org.hibernate.sql.JoinFragment;
 import org.hibernate.sql.Select;
 import org.hibernate.type.AssociationType;
-import org.hibernate.util.CollectionHelper;
 
 /**
  * Abstract walker for walkers which begin at an entity (criteria
@@ -76,23 +74,20 @@
 			final String whereString,
 			final String orderByString,
 			final LockOptions lockOptions) throws MappingException {
+		initAll( whereString, orderByString, lockOptions, AssociationInitCallback.NO_CALLBACK );
+	}
+
+	protected final void initAll(
+			final String whereString,
+			final String orderByString,
+			final LockOptions lockOptions,
+			final AssociationInitCallback callback) throws MappingException {
 		walkEntityTree( persister, getAlias() );
 		List allAssociations = new ArrayList();
-		allAssociations.addAll(associations);
-		allAssociations.add(
-				new OuterJoinableAssociation(
-						persister.getEntityType(),
-						null,
-						null,
-						alias,
-						JoinFragment.LEFT_OUTER_JOIN,
-						null,
-						getFactory(),
-						CollectionHelper.EMPTY_MAP
-				)
-		);
-		initPersisters(allAssociations, lockOptions);
-		initStatementString( whereString, orderByString, lockOptions);
+		allAssociations.addAll( associations );
+		allAssociations.add( OuterJoinableAssociation.createRoot( persister.getEntityType(), alias, getFactory() ) );
+		initPersisters( allAssociations, lockOptions, callback );
+		initStatementString( whereString, orderByString, lockOptions );
 	}
 
 	protected final void initProjection(
@@ -162,17 +157,18 @@
 		return isJoinedFetchEnabledInMapping( config, type );
 	}
 
-	protected final boolean isJoinFetchEnabledByProfile(OuterJoinLoadable persister, String path, int propertyNumber) {
+	protected final boolean isJoinFetchEnabledByProfile(OuterJoinLoadable persister, PropertyPath path, int propertyNumber) {
 		if ( !getLoadQueryInfluencers().hasEnabledFetchProfiles() ) {
 			// perf optimization
 			return false;
 		}
 
 		// ugh, this stuff has to be made easier...
+		final String fullPath = path.getFullPath();
 		String rootPropertyName = persister.getSubclassPropertyName( propertyNumber );
-		int pos = path.lastIndexOf( rootPropertyName );
+		int pos = fullPath.lastIndexOf( rootPropertyName );
 		String relativePropertyPath = pos >= 0
-				? path.substring( pos )
+				? fullPath.substring( pos )
 				: rootPropertyName;
 		String fetchRole = persister.getEntityName() + "." + relativePropertyPath;
 

Modified: core/trunk/core/src/main/java/org/hibernate/loader/JoinWalker.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/JoinWalker.java	2010-05-19 18:29:45 UTC (rev 19562)
+++ core/trunk/core/src/main/java/org/hibernate/loader/JoinWalker.java	2010-05-20 05:37:43 UTC (rev 19563)
@@ -40,8 +40,6 @@
 import org.hibernate.engine.JoinHelper;
 import org.hibernate.engine.SessionFactoryImplementor;
 import org.hibernate.engine.LoadQueryInfluencers;
-import org.hibernate.engine.profile.FetchProfile;
-import org.hibernate.engine.profile.Fetch;
 import org.hibernate.persister.collection.CollectionPersister;
 import org.hibernate.persister.collection.QueryableCollection;
 import org.hibernate.persister.entity.EntityPersister;
@@ -93,7 +91,8 @@
 		this.loadQueryInfluencers = loadQueryInfluencers;
 
 	}
-	
+
+
 	public String[] getCollectionSuffixes() {
 		return collectionSuffixes;
 	}
@@ -194,7 +193,7 @@
 			final AssociationType type,
 			final String[] aliasedLhsColumns,
 			final String alias,
-			final String path,
+			final PropertyPath path,
 			int currentDepth,
 			final int joinType) throws MappingException {
 		if ( joinType >= 0 ) {
@@ -209,7 +208,7 @@
 		}
 	}
 
-	protected String getWithClause(String path)	{
+	protected String getWithClause(PropertyPath path)	{
 		return "";
 	}
 	
@@ -221,7 +220,7 @@
 			final AssociationType type,
 			final String[] aliasedLhsColumns,
 			final String alias,
-			String path,
+			final PropertyPath path,
 			final int currentDepth,
 			final int joinType) throws MappingException {
 
@@ -236,6 +235,7 @@
 		// only need to worry about restrictions (and not say adding more
 		// joins)
 		OuterJoinableAssociation assoc = new OuterJoinableAssociation(
+				path,
 				type, 
 				alias, 
 				aliasedLhsColumns, 
@@ -245,7 +245,7 @@
 				getFactory(),
 				loadQueryInfluencers.getEnabledFilters()
 		);
-		assoc.validateJoin( path );
+		assoc.validateJoin( path.getFullPath() );
 		associations.add( assoc );
 
 		int nextDepth = currentDepth + 1;
@@ -277,7 +277,7 @@
 	 * Walk the association tree for an entity, adding associations which should
 	 * be join fetched to the {@link #associations} inst var.  This form is the
 	 * entry point into the walking for a given entity, starting the recursive
-	 * calls into {@link #walkEntityTree(OuterJoinLoadable, String, String, int)}.
+	 * calls into {@link #walkEntityTree(org.hibernate.persister.entity.OuterJoinLoadable, String, PropertyPath ,int)}.
 	 *
 	 * @param persister The persister representing the entity to be walked.
 	 * @param alias The (root) alias to use for this entity/persister.
@@ -286,15 +286,14 @@
 	protected final void walkEntityTree(
 			OuterJoinLoadable persister,
 			String alias) throws MappingException {
-		walkEntityTree( persister, alias, "", 0 );
+		walkEntityTree( persister, alias, new PropertyPath(), 0 );
 	}
 
 	/**
 	 * For a collection role, return a list of associations to be fetched by outerjoin
 	 */
-	protected final void walkCollectionTree(QueryableCollection persister, String alias)
-	throws MappingException {
-		walkCollectionTree(persister, alias, "", 0);
+	protected final void walkCollectionTree(QueryableCollection persister, String alias) throws MappingException {
+		walkCollectionTree( persister, alias, new PropertyPath(), 0 );
 		//TODO: when this is the entry point, we should use an INNER_JOIN for fetching the many-to-many elements!
 	}
 
@@ -302,11 +301,10 @@
 	 * For a collection role, return a list of associations to be fetched by outerjoin
 	 */
 	private void walkCollectionTree(
-		final QueryableCollection persister,
-		final String alias,
-		final String path,
-		final int currentDepth)
-	throws MappingException {
+			final QueryableCollection persister,
+			final String alias,
+			final PropertyPath path,
+			final int currentDepth)	throws MappingException {
 
 		if ( persister.isOneToMany() ) {
 			walkEntityTree(
@@ -338,7 +336,7 @@
 						!useInnerJoin,
 						currentDepth - 1, 
 						null //operations which cascade as far as the collection also cascade to collection elements
-					);
+				);
 				addAssociationToJoinTreeIfNecessary(
 						associationType,
 						aliasedLhsColumns,
@@ -382,7 +380,7 @@
 			final OuterJoinLoadable persister,
 			final int propertyNumber,
 			final String alias,
-			final String path,
+			final PropertyPath path,
 			final boolean nullable,
 			final int currentDepth) throws MappingException {
 		String[] aliasedLhsColumns = JoinHelper.getAliasedLHSColumnNames(
@@ -393,10 +391,10 @@
 		);
 		String lhsTable = JoinHelper.getLHSTableName(associationType, propertyNumber, persister);
 
-		String subpath = subPath( path, persister.getSubclassPropertyName(propertyNumber) );
+		PropertyPath subPath = path.append( persister.getSubclassPropertyName(propertyNumber) );
 		int joinType = getJoinType(
 				persister,
-				subpath,
+				subPath,
 				propertyNumber,
 				associationType,
 				persister.getFetchMode( propertyNumber ),
@@ -410,7 +408,7 @@
 				associationType,
 				aliasedLhsColumns,
 				alias,
-				subpath,
+				subPath,
 				currentDepth,
 				joinType
 		);
@@ -436,7 +434,7 @@
 	 */
 	protected int getJoinType(
 			OuterJoinLoadable persister,
-			final String path,
+			final PropertyPath path,
 			int propertyNumber,
 			AssociationType associationType,
 			FetchMode metadataFetchMode,
@@ -476,7 +474,7 @@
 	protected int getJoinType(
 			AssociationType associationType,
 			FetchMode config,
-			String path,
+			PropertyPath path,
 			String lhsTable,
 			String[] lhsColumns,
 			boolean nullable,
@@ -498,18 +496,18 @@
 	 * Walk the association tree for an entity, adding associations which should
 	 * be join fetched to the {@link #associations} inst var.  This form is the
 	 * entry point into the walking for a given entity, starting the recursive
-	 * calls into {@link #walkEntityTree(OuterJoinLoadable, String, String, int)}.
+	 * calls into {@link #walkEntityTree(org.hibernate.persister.entity.OuterJoinLoadable, String, PropertyPath ,int)}.
 	 *
 	 * @param persister The persister representing the entity to be walked.
 	 * @param alias The (root) alias to use for this entity/persister.
-	 * @param path todo this seems to be rooted at the *root* persister
+	 * @param path The property path to the entity being walked
 	 * @param currentDepth The current join depth
 	 * @throws org.hibernate.MappingException ???
 	 */
 	private void walkEntityTree(
 			final OuterJoinLoadable persister,
 			final String alias,
-			final String path,
+			final PropertyPath path,
 			final int currentDepth) throws MappingException {
 		int n = persister.countSubclassProperties();
 		for ( int i = 0; i < n; i++ ) {
@@ -527,13 +525,13 @@
 			}
 			else if ( type.isComponentType() ) {
 				walkComponentTree(
-					( AbstractComponentType ) type,
-					i,
-					0,
-					persister,
-					alias,
-					subPath( path, persister.getSubclassPropertyName(i) ),
-					currentDepth
+						( AbstractComponentType ) type,
+						i,
+						0,
+						persister,
+						alias,
+						path.append( persister.getSubclassPropertyName(i) ),
+						currentDepth
 				);
 			}
 		}
@@ -559,7 +557,7 @@
 			int begin,
 			final OuterJoinLoadable persister,
 			final String alias,
-			final String path,
+			final PropertyPath path,
 			final int currentDepth) throws MappingException {
 		Type[] types = componentType.getSubtypes();
 		String[] propertyNames = componentType.getPropertyNames();
@@ -574,11 +572,11 @@
 				);
 				String lhsTable = JoinHelper.getLHSTableName(associationType, propertyNumber, persister);
 
-				String subpath = subPath( path, propertyNames[i] );
+				final PropertyPath subPath = path.append( propertyNames[i] );
 				final boolean[] propertyNullability = componentType.getPropertyNullability();
 				final int joinType = getJoinType(
 						persister,
-						subpath,
+						subPath,
 						propertyNumber,
 						associationType,
 						componentType.getFetchMode(i),
@@ -592,21 +590,21 @@
 						associationType,
 						aliasedLhsColumns,
 						alias,
-						subpath,
+						subPath,
 						currentDepth,
 						joinType
 				);
 
 			}
 			else if ( types[i].isComponentType() ) {
-				String subpath = subPath( path, propertyNames[i] );
+				final PropertyPath subPath = path.append( propertyNames[i] );
 				walkComponentTree(
 						( AbstractComponentType ) types[i],
 						propertyNumber,
 						begin,
 						persister,
 						alias,
-						subpath,
+						subPath,
 						currentDepth
 				);
 			}
@@ -623,7 +621,7 @@
 			final String[] cols,
 			final QueryableCollection persister,
 			final String alias,
-			final String path,
+			final PropertyPath path,
 			final int currentDepth) throws MappingException {
 
 		Type[] types = compositeType.getSubtypes();
@@ -640,12 +638,12 @@
 				// (or even a property-ref) in a composite-element:
 				String[] aliasedLhsColumns = StringHelper.qualify(alias, lhsColumns);
 
-				String subpath = subPath( path, propertyNames[i] );
+				final PropertyPath subPath = path.append( propertyNames[i] );
 				final boolean[] propertyNullability = compositeType.getPropertyNullability();
 				final int joinType = getJoinType(
 						associationType,
 						compositeType.getFetchMode(i),
-						subpath,
+						subPath,
 						persister.getTableName(),
 						lhsColumns,
 						propertyNullability==null || propertyNullability[i],
@@ -656,19 +654,19 @@
 						associationType,
 						aliasedLhsColumns,
 						alias,
-						subpath,
+						subPath,
 						currentDepth,
 						joinType
 					);
 			}
 			else if ( types[i].isComponentType() ) {
-				String subpath = subPath( path, propertyNames[i] );
+				final PropertyPath subPath = path.append( propertyNames[i] );
 				walkCompositeElementTree(
 						(AbstractComponentType) types[i],
 						lhsColumns,
 						persister,
 						alias,
-						subpath,
+						subPath,
 						currentDepth
 					);
 			}
@@ -678,18 +676,6 @@
 	}
 
 	/**
-	 * Extend the path by the given property name
-	 */
-	private static String subPath(String path, String property) {
-		if ( path==null || path.length()==0) {
-			return property;
-		}
-		else {
-			return StringHelper.qualify(path, property);
-		}
-	}
-
-	/**
 	 * Use an inner join if it is a non-null association and this
 	 * is the "first" join in a series
 	 */
@@ -746,10 +732,7 @@
 		return type.isEntityType() && isJoinedFetchEnabledInMapping(config, type) ;
 	}
 	
-	protected String generateTableAlias(
-			final int n,
-			final String path,
-			final Joinable joinable) {
+	protected String generateTableAlias(final int n, final PropertyPath path, final Joinable joinable) {
 		return StringHelper.generateAlias( joinable.getName(), n );
 	}
 
@@ -761,10 +744,7 @@
 	 * Used to detect circularities in the joined graph, note that 
 	 * this method is side-effecty
 	 */
-	protected boolean isDuplicateAssociation(
-		final String foreignKeyTable, 
-		final String[] foreignKeyColumns
-	) {
+	protected boolean isDuplicateAssociation(final String foreignKeyTable, final String[] foreignKeyColumns) {
 		AssociationKey associationKey = new AssociationKey(foreignKeyColumns, foreignKeyTable);
 		return !visitedAssociationKeys.add( associationKey );
 	}
@@ -773,11 +753,7 @@
 	 * Used to detect circularities in the joined graph, note that 
 	 * this method is side-effecty
 	 */
-	protected boolean isDuplicateAssociation(
-		final String lhsTable,
-		final String[] lhsColumnNames,
-		final AssociationType type
-	) {
+	protected boolean isDuplicateAssociation(final String lhsTable, final String[] lhsColumnNames, final AssociationType type) {
 		final String foreignKeyTable;
 		final String[] foreignKeyColumns;
 		if ( type.getForeignKeyDirection()==ForeignKeyDirection.FOREIGN_KEY_FROM_PARENT ) {
@@ -815,20 +791,23 @@
 	 * Should we join this association?
 	 */
 	protected boolean isJoinable(
-		final int joinType,
-		final Set visitedAssociationKeys, 
-		final String lhsTable,
-		final String[] lhsColumnNames,
-		final AssociationType type,
-		final int depth
-	) {
-		if (joinType<0) return false;
+			final int joinType,
+			final Set visitedAssociationKeys,
+			final String lhsTable,
+			final String[] lhsColumnNames,
+			final AssociationType type,
+			final int depth) {
+
+		if ( joinType < 0 ) {
+			return false;
+		}
 		
-		if (joinType==JoinFragment.INNER_JOIN) return true;
-		
+		if ( joinType == JoinFragment.INNER_JOIN ) {
+			return true;
+		}
+
 		Integer maxFetchDepth = getFactory().getSettings().getMaximumFetchDepth();
-		final boolean tooDeep = maxFetchDepth!=null && 
-			depth >= maxFetchDepth.intValue();
+		final boolean tooDeep = maxFetchDepth!=null && depth >= maxFetchDepth.intValue();
 		
 		return !tooDeep && !isDuplicateAssociation(lhsTable, lhsColumnNames, type);
 	}
@@ -985,8 +964,22 @@
 		initPersisters( associations, new LockOptions(lockMode));
 	}
 
+	protected static interface AssociationInitCallback {
+		public static final AssociationInitCallback NO_CALLBACK = new AssociationInitCallback() {
+			public void associationProcessed(OuterJoinableAssociation oja, int position) {
+			}
+		};
+
+		public void associationProcessed(OuterJoinableAssociation oja, int position);
+	}
 	protected void initPersisters(final List associations, final LockOptions lockOptions) throws MappingException {
-		
+		initPersisters( associations, lockOptions, AssociationInitCallback.NO_CALLBACK );
+	}
+
+	protected void initPersisters(
+			final List associations,
+			final LockOptions lockOptions,
+			final AssociationInitCallback callback) throws MappingException {
 		final int joins = countEntityPersisters(associations);
 		final int collections = countCollectionPersisters(associations);
 
@@ -1013,6 +1006,7 @@
 				aliases[i] = oj.getRHSAlias();
 				owners[i] = oj.getOwner(associations);
 				ownerAssociationTypes[i] = (EntityType) oj.getJoinableType();
+				callback.associationProcessed( oj, i );
 				i++;
 				
 			}
@@ -1029,11 +1023,12 @@
 				if ( collPersister.isOneToMany() ) {
 					persisters[i] = (Loadable) collPersister.getElementPersister();
 					aliases[i] = oj.getRHSAlias();
+					callback.associationProcessed( oj, i );
 					i++;
 				}
 			}
 		}
-	
+
 		if ( ArrayHelper.isAllNegative(owners) ) owners = null;
 		if ( collectionOwners!=null && ArrayHelper.isAllNegative(collectionOwners) ) {
 			collectionOwners = null;

Modified: core/trunk/core/src/main/java/org/hibernate/loader/Loader.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/Loader.java	2010-05-19 18:29:45 UTC (rev 19562)
+++ core/trunk/core/src/main/java/org/hibernate/loader/Loader.java	2010-05-20 05:37:43 UTC (rev 19563)
@@ -173,6 +173,10 @@
 		return null;
 	}
 
+	protected int[][] getCompositeKeyManyToOneTargetIndices() {
+		return null;
+	}
+
 	/**
 	 * What lock options does this load entities with?
 	 *
@@ -599,20 +603,8 @@
 
 		final Loadable[] persisters = getEntityPersisters();
 		final int entitySpan = persisters.length;
+		extractKeysFromResultSet( persisters, queryParameters, resultSet, session, keys, lockModesArray, hydratedObjects );
 
-		for ( int i = 0; i < entitySpan; i++ ) {
-			keys[i] = getKeyFromResultSet(
-			        i,
-					persisters[i],
-					i == entitySpan - 1 ?
-							queryParameters.getOptionalId() :
-							null,
-					resultSet,
-					session
-				);
-			//TODO: the i==entitySpan-1 bit depends upon subclass implementation (very bad)
-		}
-
 		registerNonExists( keys, persisters, session );
 
 		// this call is side-effecty
@@ -648,6 +640,98 @@
 
 	}
 
+	protected void extractKeysFromResultSet(
+			Loadable[] persisters,
+			QueryParameters queryParameters,
+			ResultSet resultSet,
+			SessionImplementor session,
+			EntityKey[] keys,
+			LockMode[] lockModes,
+			List hydratedObjects) throws SQLException {
+		final int entitySpan = persisters.length;
+
+		final int numberOfPersistersToProcess;
+		final Serializable optionalId = queryParameters.getOptionalId();
+		if ( isSingleRowLoader() && optionalId != null ) {
+			keys[ entitySpan - 1 ] = new EntityKey( optionalId, persisters[ entitySpan - 1 ], session.getEntityMode() );
+			// skip the last persister below...
+			numberOfPersistersToProcess = entitySpan - 1;
+		}
+		else {
+			numberOfPersistersToProcess = entitySpan;
+		}
+
+		final Object[] hydratedKeyState = new Object[numberOfPersistersToProcess];
+
+		for ( int i = 0; i < numberOfPersistersToProcess; i++ ) {
+			final Type idType = persisters[i].getIdentifierType();
+			hydratedKeyState[i] = idType.hydrate( resultSet, getEntityAliases()[i].getSuffixedKeyAliases(), session, null );
+		}
+
+		for ( int i = 0; i < numberOfPersistersToProcess; i++ ) {
+			final Type idType = persisters[i].getIdentifierType();
+			if ( idType.isComponentType() && getCompositeKeyManyToOneTargetIndices() != null ) {
+				// we may need to force resolve any key-many-to-one(s)
+				int[] keyManyToOneTargetIndices = getCompositeKeyManyToOneTargetIndices()[i];
+				// todo : better solution is to order the index processing based on target indices
+				//		that would account for multiple levels whereas this scheme does not
+				if ( keyManyToOneTargetIndices != null ) {
+					for ( int targetIndex : keyManyToOneTargetIndices ) {
+						if ( targetIndex < numberOfPersistersToProcess ) {
+							final Type targetIdType = persisters[targetIndex].getIdentifierType();
+							final Serializable targetId = (Serializable) targetIdType.resolve(
+									hydratedKeyState[targetIndex],
+									session,
+									null
+							);
+							// todo : need a way to signal that this key is resolved and its data resolved
+							keys[targetIndex] = new EntityKey( targetId, persisters[targetIndex], session.getEntityMode() );
+						}
+
+						// this part copied from #getRow, this section could be refactored out
+						Object object = session.getEntityUsingInterceptor( keys[targetIndex] );
+						if ( object != null ) {
+							//its already loaded so don't need to hydrate it
+							instanceAlreadyLoaded(
+									resultSet,
+									targetIndex,
+									persisters[targetIndex],
+									keys[targetIndex],
+									object,
+									lockModes[targetIndex],
+									session
+							);
+						}
+						else {
+							object = instanceNotYetLoaded(
+									resultSet,
+									targetIndex,
+									persisters[targetIndex],
+									getEntityAliases()[targetIndex].getRowIdAlias(),
+									keys[targetIndex],
+									lockModes[targetIndex],
+									getOptionalObjectKey( queryParameters, session ),
+									queryParameters.getOptionalObject(),
+									hydratedObjects,
+									session
+							);
+						}
+					}
+				}
+			}
+			final Serializable resolvedId = (Serializable) idType.resolve( hydratedKeyState[i], session, null );
+			keys[i] = resolvedId == null ? null : new EntityKey( resolvedId, persisters[i], session.getEntityMode() );
+		}
+	}
+
+	private Serializable determineResultId(SessionImplementor session, Serializable optionalId, Type idType, Serializable resolvedId) {
+		final boolean idIsResultId = optionalId != null
+				&& resolvedId != null
+				&& idType.isEqual( optionalId, resolvedId, session.getEntityMode(), factory );
+		final Serializable resultId = idIsResultId ? optionalId : resolvedId;
+		return resultId;
+	}
+
 	protected void applyPostLoadLocks(Object[] row, LockMode[] lockModesArray, SessionImplementor session) {
 	}
 

Modified: core/trunk/core/src/main/java/org/hibernate/loader/OuterJoinableAssociation.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/OuterJoinableAssociation.java	2010-05-19 18:29:45 UTC (rev 19562)
+++ core/trunk/core/src/main/java/org/hibernate/loader/OuterJoinableAssociation.java	2010-05-20 05:37:43 UTC (rev 19563)
@@ -1,10 +1,10 @@
 /*
  * Hibernate, Relational Persistence for Idiomatic Java
  *
- * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
  * indicated by the @author tags or express copyright attribution
  * statements applied by the authors.  All third-party contributions are
- * distributed under license by Red Hat Middleware LLC.
+ * distributed under license by Red Hat Inc.
  *
  * This copyrighted material is made available to anyone wishing to use, modify,
  * copy, or redistribute it subject to the terms and conditions of the GNU
@@ -20,7 +20,6 @@
  * Free Software Foundation, Inc.
  * 51 Franklin Street, Fifth Floor
  * Boston, MA  02110-1301  USA
- *
  */
 package org.hibernate.loader;
 
@@ -35,6 +34,7 @@
 import org.hibernate.sql.JoinFragment;
 import org.hibernate.type.AssociationType;
 import org.hibernate.type.EntityType;
+import org.hibernate.util.CollectionHelper;
 
 /**
  * Part of the Hibernate SQL rendering internals.  This class represents
@@ -43,6 +43,7 @@
  * @author Gavin King
  */
 public final class OuterJoinableAssociation {
+	private final PropertyPath propertyPath;
 	private final AssociationType joinableType;
 	private final Joinable joinable;
 	private final String lhsAlias; // belong to other persister
@@ -53,7 +54,25 @@
 	private final String on;
 	private final Map enabledFilters;
 
+	public static OuterJoinableAssociation createRoot(
+			AssociationType joinableType,
+			String alias,
+			SessionFactoryImplementor factory) {
+		return new OuterJoinableAssociation(
+				new PropertyPath(),
+				joinableType,
+				null,
+				null,
+				alias,
+				JoinFragment.LEFT_OUTER_JOIN,
+				null,
+				factory,
+				CollectionHelper.EMPTY_MAP
+		);
+	}
+
 	public OuterJoinableAssociation(
+			PropertyPath propertyPath,
 			AssociationType joinableType,
 			String lhsAlias,
 			String[] lhsColumns,
@@ -62,6 +81,7 @@
 			String withClause,
 			SessionFactoryImplementor factory,
 			Map enabledFilters) throws MappingException {
+		this.propertyPath = propertyPath;
 		this.joinableType = joinableType;
 		this.lhsAlias = lhsAlias;
 		this.lhsColumns = lhsColumns;
@@ -74,14 +94,26 @@
 		this.enabledFilters = enabledFilters; // needed later for many-to-many/filter application
 	}
 
+	public PropertyPath getPropertyPath() {
+		return propertyPath;
+	}
+
 	public int getJoinType() {
 		return joinType;
 	}
 
+	public String getLhsAlias() {
+		return lhsAlias;
+	}
+
 	public String getRHSAlias() {
 		return rhsAlias;
 	}
 
+	public String getRhsAlias() {
+		return rhsAlias;
+	}
+
 	private boolean isOneToOne() {
 		if ( joinableType.isEntityType() )  {
 			EntityType etype = (EntityType) joinableType;
@@ -90,7 +122,6 @@
 		else {
 			return false;
 		}
-			
 	}
 	
 	public AssociationType getJoinableType() {
@@ -150,12 +181,8 @@
 	}
 
 	public void validateJoin(String path) throws MappingException {
-		if (
-			rhsColumns==null || 
-			lhsColumns==null ||
-			lhsColumns.length!=rhsColumns.length ||
-			lhsColumns.length==0
-		) {
+		if ( rhsColumns==null || lhsColumns==null
+				|| lhsColumns.length!=rhsColumns.length || lhsColumns.length==0 ) {
 			throw new MappingException("invalid join columns for association: " + path);
 		}
 	}

Added: core/trunk/core/src/main/java/org/hibernate/loader/PropertyPath.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/PropertyPath.java	                        (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/loader/PropertyPath.java	2010-05-20 05:37:43 UTC (rev 19563)
@@ -0,0 +1,90 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2010, Red Hat Inc. or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.loader;
+
+import org.hibernate.util.StringHelper;
+
+/**
+* TODO : javadoc
+*
+* @author Steve Ebersole
+*/
+public class PropertyPath {
+	private final PropertyPath parent;
+	private final String property;
+	private final String fullPath;
+
+	public PropertyPath(PropertyPath parent, String property) {
+		this.parent = parent;
+		this.property = property;
+
+		final String prefix;
+		if ( parent != null ) {
+			final String resolvedParent = parent.getFullPath();
+			if ( StringHelper.isEmpty( resolvedParent ) ) {
+				prefix = "";
+			}
+			else {
+				prefix = resolvedParent + '.';
+			}
+		}
+		else {
+			prefix = "";
+		}
+		this.fullPath = prefix + property;
+	}
+
+	public PropertyPath(String property) {
+		this( null, property );
+	}
+
+	public PropertyPath() {
+		this( "" );
+	}
+
+	public PropertyPath append(String property) {
+		return new PropertyPath( this, property );
+	}
+
+	public PropertyPath getParent() {
+		return parent;
+	}
+
+	public String getProperty() {
+		return property;
+	}
+
+	public String getFullPath() {
+		return fullPath;
+	}
+
+	public boolean isRoot() {
+		return parent == null && StringHelper.isEmpty( property );
+	}
+
+	@Override
+	public String toString() {
+		return getClass().getSimpleName() + '[' + fullPath + ']';
+	}
+}

Modified: core/trunk/core/src/main/java/org/hibernate/loader/collection/BasicCollectionJoinWalker.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/collection/BasicCollectionJoinWalker.java	2010-05-19 18:29:45 UTC (rev 19562)
+++ core/trunk/core/src/main/java/org/hibernate/loader/collection/BasicCollectionJoinWalker.java	2010-05-20 05:37:43 UTC (rev 19563)
@@ -27,8 +27,6 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
 import org.hibernate.FetchMode;
 import org.hibernate.LockMode;
@@ -38,12 +36,12 @@
 import org.hibernate.engine.CascadeStyle;
 import org.hibernate.loader.BasicLoader;
 import org.hibernate.loader.OuterJoinableAssociation;
+import org.hibernate.loader.PropertyPath;
 import org.hibernate.persister.collection.QueryableCollection;
 import org.hibernate.persister.entity.OuterJoinLoadable;
 import org.hibernate.sql.JoinFragment;
 import org.hibernate.sql.Select;
 import org.hibernate.type.AssociationType;
-import org.hibernate.util.CollectionHelper;
 import org.hibernate.util.StringHelper;
 
 /**
@@ -73,18 +71,7 @@
 
 		List allAssociations = new ArrayList();
 		allAssociations.addAll(associations);
-		allAssociations.add(
-				new OuterJoinableAssociation(
-						collectionPersister.getCollectionType(),
-						null,
-						null,
-						alias,
-						JoinFragment.LEFT_OUTER_JOIN,
-						null,
-						getFactory(),
-						CollectionHelper.EMPTY_MAP
-				)
-		);
+		allAssociations.add( OuterJoinableAssociation.createRoot( collectionPersister.getCollectionType(), alias, getFactory() ) );
 		initPersisters(allAssociations, LockMode.NONE);
 		initStatementString(alias, batchSize, subquery);
 	}
@@ -155,7 +142,7 @@
 
 	protected int getJoinType(
 			OuterJoinLoadable persister,
-			String path,
+			PropertyPath path,
 			int propertyNumber,
 			AssociationType associationType,
 			FetchMode metadataFetchMode,
@@ -177,7 +164,7 @@
 				currentDepth
 		);
 		//we can use an inner join for the many-to-many
-		if ( joinType==JoinFragment.LEFT_OUTER_JOIN && "".equals(path) ) {
+		if ( joinType==JoinFragment.LEFT_OUTER_JOIN && path.isRoot() ) {
 			joinType=JoinFragment.INNER_JOIN;
 		}
 		return joinType;

Modified: core/trunk/core/src/main/java/org/hibernate/loader/collection/OneToManyJoinWalker.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/collection/OneToManyJoinWalker.java	2010-05-19 18:29:45 UTC (rev 19562)
+++ core/trunk/core/src/main/java/org/hibernate/loader/collection/OneToManyJoinWalker.java	2010-05-20 05:37:43 UTC (rev 19563)
@@ -79,17 +79,7 @@
 
 		List allAssociations = new ArrayList();
 		allAssociations.addAll(associations);
-		allAssociations.add( new OuterJoinableAssociation( 
-				oneToManyPersister.getCollectionType(),
-				null, 
-				null, 
-				alias, 
-				JoinFragment.LEFT_OUTER_JOIN, 
-				null,
-				getFactory(), 
-				CollectionHelper.EMPTY_MAP 
-			) );
-		
+		allAssociations.add( OuterJoinableAssociation.createRoot( oneToManyPersister.getCollectionType(), alias, getFactory() ) );
 		initPersisters(allAssociations, LockMode.NONE);
 		initStatementString(elementPersister, alias, batchSize, subquery);
 	}

Modified: core/trunk/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java	2010-05-19 18:29:45 UTC (rev 19562)
+++ core/trunk/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java	2010-05-20 05:37:43 UTC (rev 19563)
@@ -30,7 +30,6 @@
 
 import org.hibernate.Criteria;
 import org.hibernate.FetchMode;
-import org.hibernate.LockMode;
 import org.hibernate.MappingException;
 import org.hibernate.LockOptions;
 import org.hibernate.engine.CascadeStyle;
@@ -38,12 +37,12 @@
 import org.hibernate.engine.LoadQueryInfluencers;
 import org.hibernate.impl.CriteriaImpl;
 import org.hibernate.loader.AbstractEntityJoinWalker;
+import org.hibernate.loader.PropertyPath;
 import org.hibernate.persister.entity.Joinable;
 import org.hibernate.persister.entity.OuterJoinLoadable;
 import org.hibernate.persister.entity.Queryable;
 import org.hibernate.type.AssociationType;
 import org.hibernate.type.Type;
-import org.hibernate.type.TypeFactory;
 import org.hibernate.util.ArrayHelper;
 
 /**
@@ -121,7 +120,7 @@
 
 	protected int getJoinType(
 			OuterJoinLoadable persister,
-			final String path,
+			final PropertyPath path,
 			int propertyNumber,
 			AssociationType associationType,
 			FetchMode metadataFetchMode,
@@ -130,15 +129,15 @@
 			String[] lhsColumns,
 			final boolean nullable,
 			final int currentDepth) throws MappingException {
-		if ( translator.isJoin( path ) ) {
-			return translator.getJoinType( path );
+		if ( translator.isJoin( path.getFullPath() ) ) {
+			return translator.getJoinType( path.getFullPath() );
 		}
 		else {
 			if ( translator.hasProjection() ) {
 				return -1;
 			}
 			else {
-				FetchMode fetchMode = translator.getRootCriteria().getFetchMode( path );
+				FetchMode fetchMode = translator.getRootCriteria().getFetchMode( path.getFullPath() );
 				if ( isDefaultFetchMode( fetchMode ) ) {
 					if ( isJoinFetchEnabledByProfile( persister, path, propertyNumber ) ) {
 						return getJoinType( nullable, currentDepth );
@@ -174,14 +173,14 @@
 	protected int getJoinType(
 			AssociationType associationType,
 			FetchMode config,
-			String path,
+			PropertyPath path,
 			String lhsTable,
 			String[] lhsColumns,
 			boolean nullable,
 			int currentDepth,
 			CascadeStyle cascadeStyle) throws MappingException {
-		return ( translator.isJoin( path ) ?
-				translator.getJoinType( path ) :
+		return ( translator.isJoin( path.getFullPath() ) ?
+				translator.getJoinType( path.getFullPath() ) :
 				super.getJoinType(
 						associationType,
 						config,
@@ -208,9 +207,9 @@
 			( (Queryable) getPersister() ).filterFragment( getAlias(), getLoadQueryInfluencers().getEnabledFilters() );
 	}
 	
-	protected String generateTableAlias(int n, String path, Joinable joinable) {
+	protected String generateTableAlias(int n, PropertyPath path, Joinable joinable) {
 		if ( joinable.consumesEntityAlias() ) {
-			final Criteria subcriteria = translator.getCriteria(path);
+			final Criteria subcriteria = translator.getCriteria( path.getFullPath() );
 			String sqlAlias = subcriteria==null ? null : translator.getSQLAlias(subcriteria);
 			if (sqlAlias!=null) {
 				userAliasList.add( subcriteria.getAlias() ); //alias may be null
@@ -235,8 +234,8 @@
 		return "criteria query";
 	}
 
-	protected String getWithClause(String path) {
-		return translator.getWithClause(path); 
+	protected String getWithClause(PropertyPath path) {
+		return translator.getWithClause( path.getFullPath() );
 	}
 	
 }

Modified: core/trunk/core/src/main/java/org/hibernate/loader/entity/EntityJoinWalker.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/entity/EntityJoinWalker.java	2010-05-19 18:29:45 UTC (rev 19562)
+++ core/trunk/core/src/main/java/org/hibernate/loader/entity/EntityJoinWalker.java	2010-05-20 05:37:43 UTC (rev 19563)
@@ -24,18 +24,27 @@
  */
 package org.hibernate.loader.entity;
 
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 
 import org.hibernate.FetchMode;
 import org.hibernate.LockMode;
+import org.hibernate.LockOptions;
 import org.hibernate.MappingException;
-import org.hibernate.LockOptions;
 import org.hibernate.engine.CascadeStyle;
+import org.hibernate.engine.LoadQueryInfluencers;
 import org.hibernate.engine.SessionFactoryImplementor;
-import org.hibernate.engine.LoadQueryInfluencers;
 import org.hibernate.loader.AbstractEntityJoinWalker;
+import org.hibernate.loader.OuterJoinableAssociation;
+import org.hibernate.loader.PropertyPath;
+import org.hibernate.persister.collection.QueryableCollection;
+import org.hibernate.persister.entity.EntityPersister;
 import org.hibernate.persister.entity.OuterJoinLoadable;
 import org.hibernate.type.AssociationType;
+import org.hibernate.type.ComponentType;
+import org.hibernate.type.EntityType;
+import org.hibernate.type.Type;
 
 /**
  * A walker for loaders that fetch entities
@@ -46,13 +55,14 @@
 public class EntityJoinWalker extends AbstractEntityJoinWalker {
 	
 	private final LockOptions lockOptions = new LockOptions();
+	private final int[][] compositeKeyManyToOneTargetIndices;
 
 	public EntityJoinWalker(
 			OuterJoinLoadable persister, 
 			String[] uniqueKey, 
 			int batchSize, 
 			LockMode lockMode,
-			SessionFactoryImplementor factory, 
+			final SessionFactoryImplementor factory,
 			LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
 		super( persister, factory, loadQueryInfluencers );
 
@@ -62,7 +72,9 @@
 				//include the discriminator and class-level where, but not filters
 				.append( persister.filterFragment( getAlias(), Collections.EMPTY_MAP ) );
 
-		initAll( whereCondition.toString(), "", lockOptions);
+		AssociationInitCallbackImpl callback = new AssociationInitCallbackImpl( factory );
+		initAll( whereCondition.toString(), "", lockOptions, callback );
+		this.compositeKeyManyToOneTargetIndices = callback.resolve();
 	}
 
 	public EntityJoinWalker(
@@ -79,12 +91,14 @@
 				//include the discriminator and class-level where, but not filters
 				.append( persister.filterFragment( getAlias(), Collections.EMPTY_MAP ) );
 
-		initAll( whereCondition.toString(), "", lockOptions);
+		AssociationInitCallbackImpl callback = new AssociationInitCallbackImpl( factory );
+		initAll( whereCondition.toString(), "", lockOptions, callback );
+		this.compositeKeyManyToOneTargetIndices = callback.resolve();
 	}
 
 	protected int getJoinType(
 			OuterJoinLoadable persister,
-			String path,
+			PropertyPath path,
 			int propertyNumber,
 			AssociationType associationType,
 			FetchMode metadataFetchMode,
@@ -116,5 +130,132 @@
 	public String getComment() {
 		return "load " + getPersister().getEntityName();
 	}
-	
+
+	public int[][] getCompositeKeyManyToOneTargetIndices() {
+		return compositeKeyManyToOneTargetIndices;
+	}
+
+	private static class AssociationInitCallbackImpl implements AssociationInitCallback {
+		private final SessionFactoryImplementor factory;
+		private final HashMap<String,OuterJoinableAssociation> associationsByAlias
+				= new HashMap<String, OuterJoinableAssociation>();
+		private final HashMap<String,Integer> positionsByAlias = new HashMap<String, Integer>();
+		private final ArrayList<String> aliasesForAssociationsWithCompositesIds
+				= new ArrayList<String>();
+
+		public AssociationInitCallbackImpl(SessionFactoryImplementor factory) {
+			this.factory = factory;
+		}
+
+		public void associationProcessed(OuterJoinableAssociation oja, int position) {
+			associationsByAlias.put( oja.getRhsAlias(), oja );
+			positionsByAlias.put( oja.getRhsAlias(), position );
+			EntityPersister entityPersister = null;
+			if ( oja.getJoinableType().isCollectionType() ) {
+				entityPersister = ( ( QueryableCollection) oja.getJoinable() ).getElementPersister();
+			}
+			else if ( oja.getJoinableType().isEntityType() ) {
+				entityPersister = ( EntityPersister ) oja.getJoinable();
+			}
+			if ( entityPersister != null
+					&& entityPersister.getIdentifierType().isComponentType()
+					&& ! entityPersister.getEntityMetamodel().getIdentifierProperty().isEmbedded()
+					&& hasAssociation( (ComponentType) entityPersister.getIdentifierType() ) ) {
+				aliasesForAssociationsWithCompositesIds.add( oja.getRhsAlias() );
+			}
+		}
+
+		private boolean hasAssociation(ComponentType componentType) {
+			int i = 0;
+			for ( Type subType : componentType.getSubtypes() ) {
+				if ( subType.isEntityType() ) {
+					return true;
+				}
+				else if ( subType.isComponentType() && hasAssociation( ( (ComponentType) subType ) ) ) {
+					return true;
+				}
+				i++;
+			}
+			return false;
+		}
+
+		public int[][] resolve() {
+			int[][] compositeKeyManyToOneTargetIndices = null;
+			for ( final String aliasWithCompositeId : aliasesForAssociationsWithCompositesIds ) {
+				final OuterJoinableAssociation joinWithCompositeId = associationsByAlias.get( aliasWithCompositeId );
+				final ArrayList<Integer> keyManyToOneTargetIndices = new ArrayList<Integer>();
+				// for each association with a composite id containing key-many-to-one(s), find the bidirectional side of
+				// each key-many-to-one (if exists) to see if it is eager as well.  If so, we need to track the indices
+				EntityPersister entityPersister = null;
+				if ( joinWithCompositeId.getJoinableType().isCollectionType() ) {
+					entityPersister = ( ( QueryableCollection) joinWithCompositeId.getJoinable() ).getElementPersister();
+				}
+				else if ( joinWithCompositeId.getJoinableType().isEntityType() ) {
+					entityPersister = ( EntityPersister ) joinWithCompositeId.getJoinable();
+				}
+
+				findKeyManyToOneTargetIndices(
+						keyManyToOneTargetIndices,
+						joinWithCompositeId,
+						(ComponentType) entityPersister.getIdentifierType()
+				);
+
+				if ( ! keyManyToOneTargetIndices.isEmpty() ) {
+					if ( compositeKeyManyToOneTargetIndices == null ) {
+						compositeKeyManyToOneTargetIndices = new int[ associationsByAlias.size() ][];
+					}
+					int position = positionsByAlias.get( aliasWithCompositeId );
+					compositeKeyManyToOneTargetIndices[position] = new int[ keyManyToOneTargetIndices.size() ];
+					int i = 0;
+					for ( int index : keyManyToOneTargetIndices ) {
+						compositeKeyManyToOneTargetIndices[position][i] = index;
+						i++;
+					}
+				}
+			}
+			return compositeKeyManyToOneTargetIndices;
+		}
+
+		private void findKeyManyToOneTargetIndices(
+				ArrayList<Integer> keyManyToOneTargetIndices,
+				OuterJoinableAssociation joinWithCompositeId,
+				ComponentType componentType) {
+			for ( Type subType : componentType.getSubtypes() ) {
+				if ( subType.isEntityType() ) {
+					Integer index = locateKeyManyToOneTargetIndex( joinWithCompositeId, (EntityType) subType );
+					if ( index != null ) {
+						keyManyToOneTargetIndices.add( index );
+					}
+				}
+				else if ( subType.isComponentType() ) {
+					findKeyManyToOneTargetIndices(
+							keyManyToOneTargetIndices,
+							joinWithCompositeId,
+							(ComponentType) subType
+					);
+				}
+			}
+		}
+
+		private Integer locateKeyManyToOneTargetIndex(OuterJoinableAssociation joinWithCompositeId, EntityType keyManyToOneType) {
+			// the lhs (if one) is a likely candidate
+			if ( joinWithCompositeId.getLhsAlias() != null ) {
+				final OuterJoinableAssociation lhs = associationsByAlias.get( joinWithCompositeId.getLhsAlias() );
+				if ( keyManyToOneType.getAssociatedEntityName( factory ).equals( lhs.getJoinableType().getAssociatedEntityName( factory ) ) ) {
+					return positionsByAlias.get( lhs.getRhsAlias() );
+				}
+			}
+			// otherwise, seek out OuterJoinableAssociation which are RHS of given OuterJoinableAssociation
+			// (joinWithCompositeId)
+			for ( OuterJoinableAssociation oja : associationsByAlias.values() ) {
+				if ( oja.getLhsAlias() != null && oja.getLhsAlias().equals( joinWithCompositeId.getRhsAlias() ) ) {
+					if ( keyManyToOneType.equals( oja.getJoinableType() ) ) {
+						return positionsByAlias.get( oja.getLhsAlias() );
+					}
+				}
+			}
+			return null;
+		}
+	}
+
 }
\ No newline at end of file

Modified: core/trunk/core/src/main/java/org/hibernate/loader/entity/EntityLoader.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/entity/EntityLoader.java	2010-05-19 18:29:45 UTC (rev 19562)
+++ core/trunk/core/src/main/java/org/hibernate/loader/entity/EntityLoader.java	2010-05-20 05:37:43 UTC (rev 19563)
@@ -24,14 +24,12 @@
  */
 package org.hibernate.loader.entity;
 
-import org.hibernate.HibernateException;
 import org.hibernate.LockMode;
+import org.hibernate.LockOptions;
 import org.hibernate.MappingException;
-import org.hibernate.LockOptions;
+import org.hibernate.engine.LoadQueryInfluencers;
 import org.hibernate.engine.SessionFactoryImplementor;
 import org.hibernate.engine.SessionImplementor;
-import org.hibernate.engine.LoadQueryInfluencers;
-import org.hibernate.loader.JoinWalker;
 import org.hibernate.persister.entity.OuterJoinLoadable;
 import org.hibernate.type.Type;
 
@@ -46,6 +44,7 @@
 public class EntityLoader extends AbstractEntityLoader {
 	
 	private final boolean batchLoader;
+	private final int[][] compositeKeyManyToOneTargetIndices;
 	
 	public EntityLoader(
 			OuterJoinLoadable persister, 
@@ -107,7 +106,7 @@
 			LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
 		super( persister, uniqueKeyType, factory, loadQueryInfluencers );
 
-		JoinWalker walker = new EntityJoinWalker(
+		EntityJoinWalker walker = new EntityJoinWalker(
 				persister, 
 				uniqueKey, 
 				batchSize, 
@@ -116,7 +115,7 @@
 				loadQueryInfluencers
 		);
 		initFromWalker( walker );
-
+		this.compositeKeyManyToOneTargetIndices = walker.getCompositeKeyManyToOneTargetIndices();
 		postInstantiate();
 
 		batchLoader = batchSize > 1;
@@ -134,7 +133,7 @@
 			LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
 		super( persister, uniqueKeyType, factory, loadQueryInfluencers );
 
-		JoinWalker walker = new EntityJoinWalker(
+		EntityJoinWalker walker = new EntityJoinWalker(
 				persister,
 				uniqueKey,
 				batchSize,
@@ -143,7 +142,7 @@
 				loadQueryInfluencers
 		);
 		initFromWalker( walker );
-
+		this.compositeKeyManyToOneTargetIndices = walker.getCompositeKeyManyToOneTargetIndices();
 		postInstantiate();
 
 		batchLoader = batchSize > 1;
@@ -163,5 +162,8 @@
 	protected boolean isSingleRowLoader() {
 		return !batchLoader;
 	}
-	
+
+	public int[][] getCompositeKeyManyToOneTargetIndices() {
+		return compositeKeyManyToOneTargetIndices;
+	}
 }
\ No newline at end of file



More information about the hibernate-commits mailing list