Java Mailing List Archive

http://www.gg3721.com/

Home » Hibernate Commits List »

[hibernate-commits] Hibernate SVN: r15091 - in core/trunk:
 core/src/main/java/org/hibernate/cfg and 17 other directories.

hibernate-commits

2008-08-15


Author LoginPost Reply
Author: steve.ebersole@(protected)
Date: 2008-08-15 17:20:15 -0400 (Fri, 15 Aug 2008)
New Revision: 15091

Added:
 core/trunk/core/src/main/java/org/hibernate/UnknownProfileException.java
 core/trunk/core/src/main/java/org/hibernate/engine/LoadQueryInfluencers.java
 core/trunk/core/src/main/java/org/hibernate/engine/profile/
 core/trunk/core/src/main/java/org/hibernate/engine/profile/Association.java
 core/trunk/core/src/main/java/org/hibernate/engine/profile/Fetch.java
 core/trunk/core/src/main/java/org/hibernate/engine/profile/FetchProfile.java
 core/trunk/core/src/main/java/org/hibernate/mapping/FetchProfile.java
 core/trunk/testsuite/src/test/java/org/hibernate/test/fetchprofiles/
 core/trunk/testsuite/src/test/java/org/hibernate/test/fetchprofiles/basic/
 core/trunk/testsuite/src/test/java/org/hibernate/test/fetchprofiles/basic/BasicFetchProfileTest.java
 core/trunk/testsuite/src/test/java/org/hibernate/test/fetchprofiles/basic/Course.java
 core/trunk/testsuite/src/test/java/org/hibernate/test/fetchprofiles/basic/CourseOffering.java
 core/trunk/testsuite/src/test/java/org/hibernate/test/fetchprofiles/basic/Department.java
 core/trunk/testsuite/src/test/java/org/hibernate/test/fetchprofiles/basic/Enrollment.java
 core/trunk/testsuite/src/test/java/org/hibernate/test/fetchprofiles/basic/Mappings.hbm.xml
 core/trunk/testsuite/src/test/java/org/hibernate/test/fetchprofiles/basic/Student.java
Modified:
 core/trunk/core/src/main/java/org/hibernate/Session.java
 core/trunk/core/src/main/java/org/hibernate/SessionFactory.java
 core/trunk/core/src/main/java/org/hibernate/cfg/Configuration.java
 core/trunk/core/src/main/java/org/hibernate/cfg/HbmBinder.java
 core/trunk/core/src/main/java/org/hibernate/cfg/Mappings.java
 core/trunk/core/src/main/java/org/hibernate/criterion/SubqueryExpression.java
 core/trunk/core/src/main/java/org/hibernate/engine/SessionFactoryImplementor.java
 core/trunk/core/src/main/java/org/hibernate/engine/SessionImplementor.java
 core/trunk/core/src/main/java/org/hibernate/impl/SessionFactoryImpl.java
 core/trunk/core/src/main/java/org/hibernate/impl/SessionImpl.java
 core/trunk/core/src/main/java/org/hibernate/impl/StatelessSessionImpl.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/OuterJoinLoader.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/BasicCollectionLoader.java
 core/trunk/core/src/main/java/org/hibernate/loader/collection/BatchingCollectionInitializer.java
 core/trunk/core/src/main/java/org/hibernate/loader/collection/CollectionJoinWalker.java
 core/trunk/core/src/main/java/org/hibernate/loader/collection/CollectionLoader.java
 core/trunk/core/src/main/java/org/hibernate/loader/collection/OneToManyJoinWalker.java
 core/trunk/core/src/main/java/org/hibernate/loader/collection/OneToManyLoader.java
 core/trunk/core/src/main/java/org/hibernate/loader/collection/SubselectCollectionLoader.java
 core/trunk/core/src/main/java/org/hibernate/loader/collection/SubselectOneToManyLoader.java
 core/trunk/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java
 core/trunk/core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java
 core/trunk/core/src/main/java/org/hibernate/loader/entity/AbstractEntityLoader.java
 core/trunk/core/src/main/java/org/hibernate/loader/entity/BatchingEntityLoader.java
 core/trunk/core/src/main/java/org/hibernate/loader/entity/CascadeEntityJoinWalker.java
 core/trunk/core/src/main/java/org/hibernate/loader/entity/CascadeEntityLoader.java
 core/trunk/core/src/main/java/org/hibernate/loader/entity/CollectionElementLoader.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
 core/trunk/core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java
 core/trunk/core/src/main/java/org/hibernate/persister/collection/BasicCollectionPersister.java
 core/trunk/core/src/main/java/org/hibernate/persister/collection/OneToManyPersister.java
 core/trunk/core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java
 core/trunk/core/src/main/java/org/hibernate/persister/entity/Loadable.java
 core/trunk/core/src/main/resources/org/hibernate/hibernate-mapping-3.0.dtd
 core/trunk/jmx/src/main/java/org/hibernate/jmx/SessionFactoryStub.java
 core/trunk/testsuite/src/test/resources/hibernate.properties
Log:
HHH-3414 : fetch profiles, phase 1 : join fetching

Modified: core/trunk/core/src/main/java/org/hibernate/Session.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/Session.java  2008-08-15 21:03:38 UTC (rev 15090)
+++ core/trunk/core/src/main/java/org/hibernate/Session.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@
  * @see #disconnect()
  */
 void reconnect(Connection connection) throws HibernateException;
+
+  /**
+   * Is a particular fetch profile enabled on this session?
+   *
+   * @param name The name of the profile to be checked.
+   * @return True if fetch profile is enabled; false if not.
+   * @throws UnknownProfileException Indicates that the given name does not
+   * match any known profile names
+   *
+   * @see org.hibernate.engine.profile.FetchProfile for discussion of this feature
+   */
+  public boolean isFetchProfileEnabled(String name) throws UnknownProfileException;
+
+  /**
+   * Enable a particular fetch profile on this session. No-op if requested
+   * profile is already enabled.
+   *
+   * @param name The name of the fetch profile to be enabled.
+   * @throws UnknownProfileException Indicates that the given name does not
+   * match any known profile names
+   *
+   * @see org.hibernate.engine.profile.FetchProfile for discussion of this feature
+   */
+  public void enableFetchProfile(String name) throws UnknownProfileException;
+
+  /**
+   * Disable a particular fetch profile on this session. No-op if requested
+   * profile is already disabled.
+   *
+   * @param name The name of the fetch profile to be disabled.
+   * @throws UnknownProfileException Indicates that the given name does not
+   * match any known profile names
+   *
+   * @see org.hibernate.engine.profile.FetchProfile for discussion of this feature
+   */
+  public void disableFetchProfile(String name) throws UnknownProfileException;
}

Modified: core/trunk/core/src/main/java/org/hibernate/SessionFactory.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/SessionFactory.java  2008-08-15 21:03:38 UTC (rev 15090)
+++ core/trunk/core/src/main/java/org/hibernate/SessionFactory.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@
  * @throws HibernateException If no filter defined with the given name.
  */
 public FilterDefinition getFilterDefinition(String filterName) throws HibernateException;
+
+  /**
+   * Determine if this session factory contains a fetch profile definition
+   * registered under the given name.
+   *
+   * @param name The name to check
+   * @return True if there is such a fetch profile; false otherwise.
+   */
+  public boolean containsFetchProfileDefition(String name);
}

Added: core/trunk/core/src/main/java/org/hibernate/UnknownProfileException.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/UnknownProfileException.java                  (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/UnknownProfileException.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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.
+ *
+ * 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;
+
+/**
+ * Used to indicate a request against an unknown profile name.
+ *
+ * @author Steve Ebersole
+ */
+public class UnknownProfileException extends HibernateException {
+  private final String name;
+
+  public UnknownProfileException(String name) {
+    super( "Unknow fetch profile [" + name + "]" );
+    this.name = name;
+  }
+
+  /**
+   * The unknown fetch profile name.
+   *
+   * @return The unknown fetch profile name.
+   */
+  public String getName() {
+    return name;
+  }
+}

Modified: core/trunk/core/src/main/java/org/hibernate/cfg/Configuration.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/cfg/Configuration.java  2008-08-15 21:03:38 UTC (rev 15090)
+++ core/trunk/core/src/main/java/org/hibernate/cfg/Configuration.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.UniqueKey;
+import org.hibernate.mapping.FetchProfile;
import org.hibernate.proxy.EntityNotFoundDelegate;
import org.hibernate.secure.JACCConfiguration;
import org.hibernate.tool.hbm2ddl.DatabaseMetadata;
@@(protected) @@
  */
 protected Map sqlResultSetMappings;
 protected Map filterDefinitions;
+  protected Map fetchProfiles;
 protected List secondPasses;
 protected List propertyReferences;
//  protected List extendsQueue;
@@(protected) @@
   entityResolver = XMLHelper.DEFAULT_DTD_RESOLVER;
   eventListeners = new EventListeners();
   filterDefinitions = new HashMap();
+    fetchProfiles = new HashMap();
//    extendsQueue = new ArrayList();
   extendsQueue = new HashMap();
   auxiliaryDatabaseObjects = new ArrayList();
@@(protected) @@
       namingStrategy,
       typeDefs,
       filterDefinitions,
+        fetchProfiles,
       extendsQueue,
       auxiliaryDatabaseObjects,
       tableNameBinding,
@@(protected) @@
   filterDefinitions.put( definition.getFilterName(), definition );
 }

+  public Map getFetchProfiles() {
+    return fetchProfiles;
+  }
+
+  public void addFetchProfile(FetchProfile fetchProfile) {
+    fetchProfiles.put( fetchProfile.getName(), fetchProfile );
+  }
+
 public void addAuxiliaryDatabaseObject(AuxiliaryDatabaseObject object) {
   auxiliaryDatabaseObjects.add( object );
 }

Modified: core/trunk/core/src/main/java/org/hibernate/cfg/HbmBinder.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/cfg/HbmBinder.java  2008-08-15 21:03:38 UTC (rev 15090)
+++ core/trunk/core/src/main/java/org/hibernate/cfg/HbmBinder.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@
import org.hibernate.mapping.UnionSubclass;
import org.hibernate.mapping.UniqueKey;
import org.hibernate.mapping.Value;
+import org.hibernate.mapping.FetchProfile;
import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
import org.hibernate.persister.entity.SingleTableEntityPersister;
import org.hibernate.persister.entity.UnionSubclassEntityPersister;
@@(protected) @@
     if ( "filter-def".equals( elementName ) ) {
       parseFilterDef( element, mappings );
     }
+      else if ( "fetch-profile".equals( elementName ) ) {
+        parseFetchProfile( element, mappings, null );
+      }
     else if ( "typedef".equals( elementName ) ) {
       bindTypeDef( element, mappings );
     }
@@(protected) @@
   bindDom4jRepresentation( node, persistentClass, mappings, inheritedMetas );
   bindMapRepresentation( node, persistentClass, mappings, inheritedMetas );

+    Iterator itr = node.elementIterator( "fetch-profile" );
+    while ( itr.hasNext() ) {
+      final Element profileElement = ( Element ) itr.next();
+      parseFetchProfile( profileElement, mappings, entityName );
+    }
+
   bindPersistentClassCommonValues( node, persistentClass, mappings, inheritedMetas );
-
 }

 private static void bindPojoRepresentation(Element node, PersistentClass entity,
@@(protected) @@
   filterable.addFilter( name, condition );
 }

+  private static void parseFetchProfile(Element element, Mappings mappings, String containingEntityName) {
+    String profileName = element.attributeValue( "name" );
+    FetchProfile profile = mappings.findOrCreateFetchProfile( profileName );
+    Iterator itr = element.elementIterator( "fetch" );
+    while ( itr.hasNext() ) {
+      final Element fetchElement = ( Element ) itr.next();
+      final String association = fetchElement.attributeValue( "association" );
+      final String style = fetchElement.attributeValue( "style" );
+      String entityName = fetchElement.attributeValue( "entity" );
+      if ( entityName == null ) {
+        entityName = containingEntityName;
+      }
+      if ( entityName == null ) {
+        throw new MappingException( "could not determine entity for fetch-profile fetch [" + profileName + "]:[" + association + "]" );
+      }
+      profile.addFetch( entityName, association, style );
+    }
+  }
+
 private static String getSubselect(Element element) {
   String subselect = element.attributeValue( "subselect" );
   if ( subselect != null ) {

Modified: core/trunk/core/src/main/java/org/hibernate/cfg/Mappings.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/cfg/Mappings.java  2008-08-15 21:03:38 UTC (rev 15090)
+++ core/trunk/core/src/main/java/org/hibernate/cfg/Mappings.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@
import org.hibernate.mapping.TypeDef;
import org.hibernate.mapping.AuxiliaryDatabaseObject;
import org.hibernate.mapping.Column;
+import org.hibernate.mapping.FetchProfile;
import org.hibernate.util.StringHelper;

/**
@@(protected) @@
 protected final List propertyReferences;
 protected final NamingStrategy namingStrategy;
 protected final Map filterDefinitions;
+  protected final Map fetchProfiles;
 protected final List auxiliaryDatabaseObjects;

 protected final Map extendsQueue;
@@(protected) @@
     final NamingStrategy namingStrategy,
     final Map typeDefs,
     final Map filterDefinitions,
+      final Map fetchProfiles,
//      final List extendsQueue,
     final Map extendsQueue,
     final List auxiliaryDatabaseObjects,
     final Map tableNamebinding,
-      final Map columnNameBindingPerTable
-      ) {
+      final Map columnNameBindingPerTable) {
   this.classes = classes;
   this.collections = collections;
   this.queries = queries;
@@(protected) @@
   this.namingStrategy = namingStrategy;
   this.typeDefs = typeDefs;
   this.filterDefinitions = filterDefinitions;
+    this.fetchProfiles = fetchProfiles;
   this.extendsQueue = extendsQueue;
   this.auxiliaryDatabaseObjects = auxiliaryDatabaseObjects;
   this.tableNameBinding = tableNamebinding;
@@(protected) @@
 public FilterDefinition getFilterDefinition(String name) {
   return (FilterDefinition) filterDefinitions.get(name);
 }
-  
+
+  public Map getFetchProfiles() {
+    return fetchProfiles;
+  }
+
+  public FetchProfile findOrCreateFetchProfile(String name) {
+    FetchProfile profile = ( FetchProfile ) fetchProfiles.get( name );
+    if ( profile == null ) {
+      profile = new FetchProfile( name );
+      fetchProfiles.put( name, profile );
+    }
+    return profile;
+  }
+
 public boolean isDefaultLazy() {
   return defaultLazy;
 }

Modified: core/trunk/core/src/main/java/org/hibernate/criterion/SubqueryExpression.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/criterion/SubqueryExpression.java  2008-08-15 21:03:38 UTC (rev 15090)
+++ core/trunk/core/src/main/java/org/hibernate/criterion/SubqueryExpression.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@
import org.hibernate.engine.QueryParameters;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.TypedValue;
+import org.hibernate.engine.LoadQueryInfluencers;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.loader.criteria.CriteriaJoinWalker;
import org.hibernate.loader.criteria.CriteriaQueryTranslator;
@@(protected) @@
       factory,
       criteriaImpl,
       criteriaImpl.getEntityOrClassName(),
-        new HashMap(),
-        innerQuery.getRootSQLALias());
+        LoadQueryInfluencers.NONE,
+        innerQuery.getRootSQLALias()
+    );

   String sql = walker.getSQLString();


Added: core/trunk/core/src/main/java/org/hibernate/engine/LoadQueryInfluencers.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/engine/LoadQueryInfluencers.java                  (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/engine/LoadQueryInfluencers.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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.
+ *
+ * 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.engine;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.HashSet;
+
+import org.hibernate.Filter;
+import org.hibernate.UnknownProfileException;
+import org.hibernate.type.Type;
+import org.hibernate.impl.FilterImpl;
+
+/**
+ * Centralize all options which can influence the SQL query needed to load and
+ * entity. Currently such influencers are defined as:<ul>
+ * <li>filters</li>
+ * <li>fetch profiles</li>
+ * <li>internal fetch profile (merge profile, etc)</li>
+ * </ul>
+ *
+ * @author Steve Ebersole
+ */
+public class LoadQueryInfluencers {
+  /**
+   * Static reference useful for cases where we are creating load SQL
+   * outside the context of any influencers. One such example is
+   * anything created by the session factory.
+   */
+  public static LoadQueryInfluencers NONE = new LoadQueryInfluencers();
+
+  private final SessionFactoryImplementor sessionFactory;
+  private String internalFetchProfile;
+  private Map enabledFilters;
+  private Set enabledFetchProfileNames;
+
+  public LoadQueryInfluencers() {
+    this( null, java.util.Collections.EMPTY_MAP, java.util.Collections.EMPTY_SET );
+  }
+
+  public LoadQueryInfluencers(SessionFactoryImplementor sessionFactory) {
+    this( sessionFactory, new HashMap(), new HashSet() );
+  }
+
+  private LoadQueryInfluencers(SessionFactoryImplementor sessionFactory, Map enabledFilters, Set enabledFetchProfileNames) {
+    this.sessionFactory = sessionFactory;
+    this.enabledFilters = enabledFilters;
+    this.enabledFetchProfileNames = enabledFetchProfileNames;
+  }
+
+  public SessionFactoryImplementor getSessionFactory() {
+    return sessionFactory;
+  }
+
+
+  // internal fetch profile support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+  public String getInternalFetchProfile() {
+    return internalFetchProfile;
+  }
+
+  public void setInternalFetchProfile(String internalFetchProfile) {
+    if ( sessionFactory == null ) {
+      // thats the signal that this is the immutable, context-less
+      // variety
+      throw new IllegalStateException( "Cannot modify context-less LoadQueryInfluencers" );
+    }
+    this.internalFetchProfile = internalFetchProfile;
+  }
+
+
+  // filter support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+  public boolean hasEnabledFilters() {
+    return enabledFilters != null && !enabledFilters.isEmpty();
+  }
+
+  public Map getEnabledFilters() {
+    // First, validate all the enabled filters...
+    //TODO: this implementation has bad performance
+    Iterator itr = enabledFilters.values().iterator();
+    while ( itr.hasNext() ) {
+      final Filter filter = ( Filter ) itr.next();
+      filter.validate();
+    }
+    return enabledFilters;
+  }
+
+  public Filter getEnabledFilter(String filterName) {
+    return ( Filter ) enabledFilters.get( filterName );
+  }
+
+  public Filter enableFilter(String filterName) {
+    FilterImpl filter = new FilterImpl( sessionFactory.getFilterDefinition( filterName ) );
+    enabledFilters.put( filterName, filter );
+    return filter;
+  }
+
+  public void disableFilter(String filterName) {
+    enabledFilters.remove( filterName );
+  }
+
+  public Object getFilterParameterValue(String filterParameterName) {
+    String[] parsed = parseFilterParameterName( filterParameterName );
+    FilterImpl filter = ( FilterImpl ) enabledFilters.get( parsed[0] );
+    if ( filter == null ) {
+      throw new IllegalArgumentException( "Filter [" + parsed[0] + "] currently not enabled" );
+    }
+    return filter.getParameter( parsed[1] );
+  }
+
+  public Type getFilterParameterType(String filterParameterName) {
+    String[] parsed = parseFilterParameterName( filterParameterName );
+    FilterDefinition filterDef = sessionFactory.getFilterDefinition( parsed[0] );
+    if ( filterDef == null ) {
+      throw new IllegalArgumentException( "Filter [" + parsed[0] + "] not defined" );
+    }
+    Type type = filterDef.getParameterType( parsed[1] );
+    if ( type == null ) {
+      // this is an internal error of some sort...
+      throw new InternalError( "Unable to locate type for filter parameter" );
+    }
+    return type;
+  }
+
+  public static String[] parseFilterParameterName(String filterParameterName) {
+    int dot = filterParameterName.indexOf( '.' );
+    if ( dot <= 0 ) {
+      throw new IllegalArgumentException( "Invalid filter-parameter name format" );
+    }
+    String filterName = filterParameterName.substring( 0, dot );
+    String parameterName = filterParameterName.substring( dot + 1 );
+    return new String[] { filterName, parameterName };
+  }
+
+
+  // fetch profile support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+  public boolean hasEnabledFetchProfiles() {
+    return enabledFetchProfileNames != null && !enabledFetchProfileNames.isEmpty();
+  }
+
+  public Set getEnabledFetchProfileNames() {
+    return enabledFetchProfileNames;
+  }
+
+  private void checkFetchProfileName(String name) {
+    if ( !sessionFactory.containsFetchProfileDefition( name ) ) {
+      throw new UnknownProfileException( name );
+    }
+  }
+
+  public boolean isFetchProfileEnabled(String name) throws UnknownProfileException {
+    checkFetchProfileName( name );
+    return enabledFetchProfileNames.contains( name );
+  }
+
+  public void enableFetchProfile(String name) throws UnknownProfileException {
+    checkFetchProfileName( name );
+    enabledFetchProfileNames.add( name );
+  }
+
+  public void disableFetchProfile(String name) throws UnknownProfileException {
+    checkFetchProfileName( name );
+    enabledFetchProfileNames.remove( name );
+  }
+
+}

Modified: core/trunk/core/src/main/java/org/hibernate/engine/SessionFactoryImplementor.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/engine/SessionFactoryImplementor.java  2008-08-15 21:03:38 UTC (rev 15090)
+++ core/trunk/core/src/main/java/org/hibernate/engine/SessionFactoryImplementor.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@
import org.hibernate.ConnectionReleaseMode;
import org.hibernate.proxy.EntityNotFoundDelegate;
import org.hibernate.engine.query.QueryPlanCache;
+import org.hibernate.engine.profile.FetchProfile;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.cache.QueryCache;
@@(protected) @@
 public EntityNotFoundDelegate getEntityNotFoundDelegate();

 public SQLFunctionRegistry getSqlFunctionRegistry();
-    
+
+  /**
+   * Retrieve fetch profile by name.
+   *
+   * @param name The name of the profile to retrieve.
+   * @return The profile definition
+   */
+  public FetchProfile getFetchProfile(String name);
+
}

Modified: core/trunk/core/src/main/java/org/hibernate/engine/SessionImplementor.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/engine/SessionImplementor.java  2008-08-15 21:03:38 UTC (rev 15090)
+++ core/trunk/core/src/main/java/org/hibernate/engine/SessionImplementor.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;

import org.hibernate.CacheMode;
import org.hibernate.EntityMode;
@@(protected) @@
  * @param filterParameterName The filter parameter name in the format
  * {FILTER_NAME.PARAMETER_NAME}.
  * @return The filter parameter value.
+   * @deprecated use #getLoadQueryInfluencers instead
  */
 public Object getFilterParameterValue(String filterParameterName);

@@(protected) @@
  *
  * @param filterParameterName The filter parameter name in the format
  * {FILTER_NAME.PARAMETER_NAME}.
+   * @return The filter param type
+   * @deprecated use #getLoadQueryInfluencers instead
  */
 public Type getFilterParameterType(String filterParameterName);

@@(protected) @@
  * name, with values corresponding to the {@(protected)}
  * instance.
  * @return The currently enabled filters.
+   * @deprecated use #getLoadQueryInfluencers instead
  */
 public Map getEnabledFilters();
 
@@(protected) @@

 public void afterScrollOperation();

+  /**
+   * Get the <i>internal</i> fetch profile currently associated with this session.
+   *
+   * @return The current internal fetch profile, or null if none currently associated.
+   * @deprecated use #getLoadQueryInfluencers instead
+   */
+  public String getFetchProfile();
+
+  /**
+   * Set the current <i>internal</i> fetch profile for this session.
+   *
+   * @param name The internal fetch profile name to use
+   * @deprecated use #getLoadQueryInfluencers instead
+   */
 public void setFetchProfile(String name);

-  public String getFetchProfile();
-
 public JDBCContext getJDBCContext();

 /**
@@(protected) @@
  * @return True if the session is closed; false otherwise.
  */
 public boolean isClosed();
+
+  /**
+   * Get the load query influencers associated with this session.
+   *
+   * @return the load query influencers associated with this session;
+   * should never be null.
+   */
+  public LoadQueryInfluencers getLoadQueryInfluencers();
}

Added: core/trunk/core/src/main/java/org/hibernate/engine/profile/Association.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/engine/profile/Association.java                  (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/engine/profile/Association.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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.
+ *
+ * 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.engine.profile;
+
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * Models the association of a given fetch.
+ *
+ * @author Steve Ebersole
+ */
+public class Association {
+  private final EntityPersister owner;
+  private final String associationPath;
+  private final String role;
+
+  public Association(EntityPersister owner, String associationPath) {
+    this.owner = owner;
+    this.associationPath = associationPath;
+    this.role = owner.getEntityName() + '.' + associationPath;
+  }
+
+  public EntityPersister getOwner() {
+    return owner;
+  }
+
+  public String getAssociationPath() {
+    return associationPath;
+  }
+
+  public String getRole() {
+    return role;
+  }
+}

Added: core/trunk/core/src/main/java/org/hibernate/engine/profile/Fetch.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/engine/profile/Fetch.java                  (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/engine/profile/Fetch.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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.
+ *
+ * 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.engine.profile;
+
+/**
+ * Models an individual fetch within a profile.
+ *
+ * @author Steve Ebersole
+ */
+public class Fetch {
+  private final Association association;
+  private final Style style;
+
+  public Fetch(Association association, Style style) {
+    this.association = association;
+    this.style = style;
+  }
+
+  public Association getAssociation() {
+    return association;
+  }
+
+  public Style getStyle() {
+    return style;
+  }
+
+  /**
+   * The type or style of fetch. For the moment we limit this to
+   * join and select, though technically subselect would be valid
+   * here as as well; however, to support subselect here would
+   * require major changes to the subselect loading code (which is
+   * needed for other things as well anyway).
+   */
+  public static class Style {
+    public static final Style JOIN = new Style( "join" );
+    public static final Style SELECT = new Style( "select" );
+
+    private final String name;
+
+    private Style(String name) {
+      this.name = name;
+    }
+
+    public String toString() {
+      return name;
+    }
+
+    public static Style parse(String name) {
+      if ( SELECT.name.equals( name ) ) {
+        return SELECT;
+      }
+      else {
+        // the default...
+        return JOIN;
+      }
+    }
+  }
+
+  public String toString() {
+    return "Fetch[" + style + "{" + association.getRole() + "}]";
+  }
+}

Added: core/trunk/core/src/main/java/org/hibernate/engine/profile/FetchProfile.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/engine/profile/FetchProfile.java                  (rev 0)
+++ core/trunk/core/src/main/java/org/hibernate/engine/profile/FetchProfile.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC 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.
+ *
+ * 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.engine.profile;
+
+import java.util.Map;
+import java.util.HashMap;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+import org.hibernate.type.BagType;
+import org.hibernate.type.AssociationType;
+
+/**
+ * A 'fetch profile' allows a user to dynamically modify the fetching
+ * strategy used for particular associations at runtime, whereas that
+ * information was historically only statically defined in the metadata.
+ *
+ * @author Steve Ebersole
+ */
+public class FetchProfile {
+  private static final Logger log = LoggerFactory.getLogger( FetchProfile.class );
+
+  private final String name;
+  private Map fetches = new HashMap();
+
+  private boolean containsJoinFetchedCollection = false;
+  private boolean containsJoinFetchedBag = false;
+  private Fetch bagJoinFetch;
+
+  /**
+   * A 'fetch profile' is uniquely named within a
+   * {@(protected)
+   * uniquely and easily identifiable within that
+   * {@(protected)}.
+   *
+   * @param name The name under which we are bound in the sessionFactory
+   */
+  public FetchProfile(String name) {
+    this.name = name;
+  }
+
+  /**
+   * Add a fetch to the profile.
+   *
+   * @param association The association to be fetched
+   * @param fetchStyleName The name of the fetch style to apply
+   */
+  public void addFetch(Association association, String fetchStyleName) {
+    addFetch( association, Fetch.Style.parse( fetchStyleName ) );
+  }
+
+  /**
+   * Add a fetch to the profile.
+   *
+   * @param association The association to be fetched
+   * @param style The style to apply
+   */
+  public void addFetch(Association association, Fetch.Style style) {
+    addFetch( new Fetch( association, style ) );
+  }
+
+  /**
+   * Add a fetch to the profile.
+   *
+   * @param fetch The fetch to add.
+   */
+  public void addFetch(Fetch fetch) {
+    Type associationType = fetch.getAssociation().getOwner().getPropertyType( fetch.getAssociation().getAssociationPath() );
+    if ( associationType.isCollectionType() ) {
+      log.trace( "handling request to add collection fetch [{}]", fetch.getAssociation().getRole() );
+
+      // couple of things for whcih to account in the case of collection
+      // join fetches
+      if ( Fetch.Style.JOIN == fetch.getStyle() ) {
+        // first, if this is a bag we need to ignore it if we previously
+        // processed collection join fetches
+        if ( BagType.class.isInstance( associationType ) ) {
+          if ( containsJoinFetchedCollection ) {
+            log.warn( "Ignoring bag join fetch [{}] due to prior collection join fetch", fetch.getAssociation().getRole() );
+            return; // EARLY EXIT!!!
+          }
+        }
+
+        // also, in cases where we are asked to add a collection join
+        // fetch where we had already added a bag join fetch previously,
+        // we need to go back and ignore that previous bag join fetch.
+        if ( containsJoinFetchedBag ) {
+          if ( fetches.remove( bagJoinFetch.getAssociation().getRole() ) != bagJoinFetch ) {
+            // just for safety...
+            log.warn( "Unable to erase previously added bag join fetch" );
+          }
+          bagJoinFetch = null;
+          containsJoinFetchedBag = false;
+        }
+
+        containsJoinFetchedCollection = true;
+      }
+    }
+    fetches.put( fetch.getAssociation().getRole(), fetch );
+  }
+
+  /**
+   * Getter for property 'name'.
+   *
+   * @return Value for property 'name'.
+   */
+  public String getName() {
+    return name;
+  }
+
+  /**
+   * Getter for property 'fetches'. Map of {@(protected),
+   * keyed by associaion <tt>role</tt>
+   *
+   * @return Value for property 'fetches'.
+   */
+  public Map getFetches() {
+    return fetches;
+  }
+
+  public Fetch getFetchByRole(String role) {
+    return ( Fetch ) fetches.get( role );
+  }
+
+  /**
+   * Getter for property 'containsJoinFetchedCollection', which flags whether
+   * this fetch profile contained any collection join fetches.
+   *
+   * @return Value for property 'containsJoinFetchedCollection'.
+   */
+  public boolean isContainsJoinFetchedCollection() {
+    return containsJoinFetchedCollection;
+  }
+
+  /**
+   * Getter for property 'containsJoinFetchedBag', which flags whether this
+   * fetch profile contained any bag join fetches
+   *
+   * @return Value for property 'containsJoinFetchedBag'.
+   */
+  public boolean isContainsJoinFetchedBag() {
+    return containsJoinFetchedBag;
+  }
+}

Modified: core/trunk/core/src/main/java/org/hibernate/impl/SessionFactoryImpl.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/impl/SessionFactoryImpl.java  2008-08-15 21:03:38 UTC (rev 15090)
+++ core/trunk/core/src/main/java/org/hibernate/impl/SessionFactoryImpl.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@
import org.hibernate.engine.NamedSQLQueryDefinition;
import org.hibernate.engine.ResultSetMappingDefinition;
import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.profile.FetchProfile;
+import org.hibernate.engine.profile.Fetch;
+import org.hibernate.engine.profile.Association;
import org.hibernate.engine.query.QueryPlanCache;
import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
import org.hibernate.event.EventListeners;
@@(protected) @@
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Queryable;
+import org.hibernate.persister.entity.Loadable;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.proxy.EntityNotFoundDelegate;
import org.hibernate.stat.Statistics;
@@(protected) @@
 private final transient Map namedSqlQueries;
 private final transient Map sqlResultSetMappings;
 private final transient Map filters;
+  private final transient Map fetchProfiles;
 private final transient Map imports;
 private final transient Interceptor interceptor;
 private final transient Settings settings;
@@(protected) @@
     public void sessionFactoryClosed(SessionFactory factory) {
     }
   };
+
   this.filters = new HashMap();
   this.filters.putAll( cfg.getFilterDefinitions() );

@@(protected) @@
   }
   this.entityNotFoundDelegate = entityNotFoundDelegate;

+    // this needs to happen after persisters are all ready to go...
+    this.fetchProfiles = new HashMap();
+    itr = cfg.getFetchProfiles().values().iterator();
+    while ( itr.hasNext() ) {
+      final org.hibernate.mapping.FetchProfile mappingProfile =
+          ( org.hibernate.mapping.FetchProfile ) itr.next();
+      final FetchProfile fetchProfile = new FetchProfile( mappingProfile.getName() );
+      Iterator fetches = mappingProfile.getFetches().iterator();
+      while ( fetches.hasNext() ) {
+        final org.hibernate.mapping.FetchProfile.Fetch mappingFetch =
+            ( org.hibernate.mapping.FetchProfile.Fetch ) fetches.next();
+        // resolve the persister owning the fetch
+        final String entityName = getImportedClassName( mappingFetch.getEntity() );
+        final EntityPersister owner = ( EntityPersister ) ( entityName == null ? null : entityPersisters.get( entityName ) );
+        if ( owner == null ) {
+          throw new HibernateException(
+              "Unable to resolve entity reference [" + mappingFetch.getEntity()
+                  + "] in fetch profile [" + fetchProfile.getName() + "]"
+          );
+        }
+
+        // validate the specified association fetch
+        Type associationType = owner.getPropertyType( mappingFetch.getAssociation() );
+        if ( associationType == null || !associationType.isAssociationType() ) {
+          throw new HibernateException( "Fetch profile [" + fetchProfile.getName() + "] specified an invalid association" );
+        }
+
+        // resolve the style
+        final Fetch.Style fetchStyle = Fetch.Style.parse( mappingFetch.getStyle() );
+
+        // then construct the fetch instance...
+        fetchProfile.addFetch( new Association( owner, mappingFetch.getAssociation() ), fetchStyle );
+        ( ( Loadable ) owner ).registerAffectingFetchProfile( fetchProfile.getName() );
+      }
+      fetchProfiles.put( fetchProfile.getName(), fetchProfile );
+    }
+
   this.observer.sessionFactoryCreated( this );
 }

@@(protected) @@
   return def;
 }

+  public boolean containsFetchProfileDefition(String name) {
+    return fetchProfiles.containsKey( name );
+  }
+
 public Set getDefinedFilterNames() {
   return filters.keySet();
 }
@@(protected) @@
   return entityNotFoundDelegate;
 }

+  public SQLFunctionRegistry getSqlFunctionRegistry() {
+    return sqlFunctionRegistry;
+  }
+
+  public FetchProfile getFetchProfile(String name) {
+    return ( FetchProfile ) fetchProfiles.get( name );
+  }
+
 /**
  * Custom serialization hook used during Session serialization.
  *
@@(protected) @@
   }
   return ( SessionFactoryImpl ) result;
 }
-
-  public SQLFunctionRegistry getSqlFunctionRegistry() {
-    return sqlFunctionRegistry;
-  }
}

Modified: core/trunk/core/src/main/java/org/hibernate/impl/SessionImpl.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/impl/SessionImpl.java  2008-08-15 21:03:38 UTC (rev 15090)
+++ core/trunk/core/src/main/java/org/hibernate/impl/SessionImpl.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@
import org.hibernate.Transaction;
import org.hibernate.TransientObjectException;
import org.hibernate.UnresolvableObjectException;
+import org.hibernate.UnknownProfileException;
import org.hibernate.collection.PersistentCollection;
import org.hibernate.engine.ActionQueue;
import org.hibernate.engine.CollectionEntry;
@@(protected) @@
import org.hibernate.engine.QueryParameters;
import org.hibernate.engine.StatefulPersistenceContext;
import org.hibernate.engine.Status;
+import org.hibernate.engine.LoadQueryInfluencers;
import org.hibernate.engine.query.FilterQueryPlan;
import org.hibernate.engine.query.HQLQueryPlan;
import org.hibernate.engine.query.NativeSQLQueryPlan;
@@(protected) @@
 private transient boolean flushBeforeCompletionEnabled;
 private transient boolean autoCloseSessionEnabled;
 private transient ConnectionReleaseMode connectionReleaseMode;
-  
-  private transient String fetchProfile;

-  private transient Map enabledFilters = new HashMap();
+  private transient LoadQueryInfluencers loadQueryInfluencers;

 private transient Session rootSession;
 private transient Map childSessionsByEntityMode;
@@(protected) @@
   this.autoCloseSessionEnabled = false;
   this.connectionReleaseMode = null;

+    loadQueryInfluencers = new LoadQueryInfluencers( factory );
+
   if ( factory.getStatistics().isStatisticsEnabled() ) {
     factory.getStatisticsImplementor().openSession();
   }
@@(protected) @@
   this.connectionReleaseMode = connectionReleaseMode;
   this.jdbcContext = new JDBCContext( this, connection, interceptor );

+    loadQueryInfluencers = new LoadQueryInfluencers( factory );
+
   if ( factory.getStatistics().isStatisticsEnabled() ) {
     factory.getStatisticsImplementor().openSession();
   }
@@(protected) @@
   flush();
 }

-  public Filter getEnabledFilter(String filterName) {
-    checkTransactionSynchStatus();
-    return (Filter) enabledFilters.get(filterName);
-  }

-  public Filter enableFilter(String filterName) {
-    errorIfClosed();
-    checkTransactionSynchStatus();
-    FilterImpl filter = new FilterImpl( factory.getFilterDefinition(filterName) );
-    enabledFilters.put(filterName, filter);
-    return filter;
-  }
-
-  public void disableFilter(String filterName) {
-    errorIfClosed();
-    checkTransactionSynchStatus();
-    enabledFilters.remove(filterName);
-  }
-
-  public Object getFilterParameterValue(String filterParameterName) {
-    errorIfClosed();
-    checkTransactionSynchStatus();
-    String[] parsed = parseFilterParameterName(filterParameterName);
-    FilterImpl filter = (FilterImpl) enabledFilters.get( parsed[0] );
-    if (filter == null) {
-      throw new IllegalArgumentException("Filter [" + parsed[0] + "] currently not enabled");
-    }
-    return filter.getParameter( parsed[1] );
-  }
-
-  public Type getFilterParameterType(String filterParameterName) {
-    errorIfClosed();
-    checkTransactionSynchStatus();
-    String[] parsed = parseFilterParameterName(filterParameterName);
-    FilterDefinition filterDef = factory.getFilterDefinition( parsed[0] );
-    if (filterDef == null) {
-      throw new IllegalArgumentException("Filter [" + parsed[0] + "] not defined");
-    }
-    Type type = filterDef.getParameterType( parsed[1] );
-    if (type == null) {
-      // this is an internal error of some sort...
-      throw new InternalError("Unable to locate type for filter parameter");
-    }
-    return type;
-  }
-
-  public Map getEnabledFilters() {
-    errorIfClosed();
-    checkTransactionSynchStatus();
-    // First, validate all the enabled filters...
-    //TODO: this implementation has bad performance
-    Iterator itr = enabledFilters.values().iterator();
-    while ( itr.hasNext() ) {
-      final Filter filter = (Filter) itr.next();
-      filter.validate();
-    }
-    return enabledFilters;
-  }
-
-  private String[] parseFilterParameterName(String filterParameterName) {
-    int dot = filterParameterName.indexOf('.');
-    if (dot <= 0) {
-      throw new IllegalArgumentException("Invalid filter-parameter name format"); // TODO: what type?
-    }
-    String filterName = filterParameterName.substring(0, dot);
-    String parameterName = filterParameterName.substring(dot+1);
-    return new String[] {filterName, parameterName};
-  }
-
-
 /**
  * Retrieve a list of persistent objects using a hibernate query
  */
@@(protected) @@
   return listFilter( collection, filter, new QueryParameters( new Type[]{null, type}, new Object[]{null, value} ) );
 }

-  public Collection filter(Object collection, String filter, Object[] values, Type[] types)
+  public Collection filter(Object collection, String filter, Object[] values, Type[] types)
 throws HibernateException {
   Object[] vals = new Object[values.length + 1];
   Type[] typs = new Type[types.length + 1];
@@(protected) @@
   return plan;
 }

-  public List listFilter(Object collection, String filter, QueryParameters queryParameters)
+  public List listFilter(Object collection, String filter, QueryParameters queryParameters)
 throws HibernateException {
   errorIfClosed();
   checkTransactionSynchStatus();
@@(protected) @@
   return results;
 }

-  public Iterator iterateFilter(Object collection, String filter, QueryParameters queryParameters)
+  public Iterator iterateFilter(Object collection, String filter, QueryParameters queryParameters)
 throws HibernateException {
   errorIfClosed();
   checkTransactionSynchStatus();
@@(protected) @@
       factory,
       criteria,
       entityName,
-        getEnabledFilters()
+        getLoadQueryInfluencers()
   );
   autoFlushIfRequired( loader.getQuerySpaces() );
   dontFlushFromFind++;
@@(protected) @@
         factory,
         criteria,
         implementors[i],
-          getEnabledFilters()
+          getLoadQueryInfluencers()
       );

     spaces.addAll( loaders[i].getQuerySpaces() );
@@(protected) @@
   );
 }

-  public ScrollableResults scrollCustomQuery(CustomQuery customQuery, QueryParameters queryParameters)
+  public ScrollableResults scrollCustomQuery(CustomQuery customQuery, QueryParameters queryParameters)
 throws HibernateException {
   errorIfClosed();
   checkTransactionSynchStatus();
@@(protected) @@
   return factory;
 }
 
-  public void initializeCollection(PersistentCollection collection, boolean writing)
+  public void initializeCollection(PersistentCollection collection, boolean writing)
 throws HibernateException {
   errorIfClosed();
   checkTransactionSynchStatus();
@@(protected) @@
   // nothing to do in a stateful session
 }

-  public String getFetchProfile() {
+  public JDBCContext getJDBCContext() {
+    errorIfClosed();
   checkTransactionSynchStatus();
-    return fetchProfile;
+    return jdbcContext;
 }

-  public JDBCContext getJDBCContext() {
+  public LoadQueryInfluencers getLoadQueryInfluencers() {
+    return loadQueryInfluencers;
+  }
+
+  // filter support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+  /**
+   * {@(protected)}
+   */
+  public Filter getEnabledFilter(String filterName) {
+    checkTransactionSynchStatus();
+    return loadQueryInfluencers.getEnabledFilter( filterName );
+  }
+
+  /**
+   * {@(protected)}
+   */
+  public Filter enableFilter(String filterName) {
   errorIfClosed();
   checkTransactionSynchStatus();
-    return jdbcContext;
+    return loadQueryInfluencers.enableFilter( filterName );
 }

+  /**
+   * {@(protected)}
+   */
+  public void disableFilter(String filterName) {
+    errorIfClosed();
+    checkTransactionSynchStatus();
+    loadQueryInfluencers.disableFilter( filterName );
+  }
+
+  /**
+   * {@(protected)}
+   */
+  public Object getFilterParameterValue(String filterParameterName) {
+    errorIfClosed();
+    checkTransactionSynchStatus();
+    return loadQueryInfluencers.getFilterParameterValue( filterParameterName );
+  }
+
+  /**
+   * {@(protected)}
+   */
+  public Type getFilterParameterType(String filterParameterName) {
+    errorIfClosed();
+    checkTransactionSynchStatus();
+    return loadQueryInfluencers.getFilterParameterType( filterParameterName );
+  }
+
+  /**
+   * {@(protected)}
+   */
+  public Map getEnabledFilters() {
+    errorIfClosed();
+    checkTransactionSynchStatus();
+    return loadQueryInfluencers.getEnabledFilters();
+  }
+
+
+  // internal fetch profile support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+  /**
+   * {@(protected)}
+   */
+  public String getFetchProfile() {
+    checkTransactionSynchStatus();
+    return loadQueryInfluencers.getInternalFetchProfile();
+  }
+
+  /**
+   * {@(protected)}
+   */
 public void setFetchProfile(String fetchProfile) {
   errorIfClosed();
   checkTransactionSynchStatus();
-    this.fetchProfile = fetchProfile;
+    loadQueryInfluencers.setInternalFetchProfile( fetchProfile );
 }

+
+  // fetch profile support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+  public boolean isFetchProfileEnabled(String name) throws UnknownProfileException {
+    return loadQueryInfluencers.isFetchProfileEnabled( name );
+  }
+
+  public void enableFetchProfile(String name) throws UnknownProfileException {
+    loadQueryInfluencers.enableFetchProfile( name );
+  }
+
+  public void disableFetchProfile(String name) throws UnknownProfileException {
+    loadQueryInfluencers.disableFetchProfile( name );
+  }
+
+
 private void checkTransactionSynchStatus() {
   if ( jdbcContext != null && !isClosed() ) {
     jdbcContext.registerSynchronizationIfPossible();
@@(protected) @@
   cacheMode = CacheMode.parse( ( String ) ois.readObject() );
   flushBeforeCompletionEnabled = ois.readBoolean();
   autoCloseSessionEnabled = ois.readBoolean();
-    fetchProfile = ( String ) ois.readObject();
   interceptor = ( Interceptor ) ois.readObject();

   factory = SessionFactoryImpl.deserialize( ois );
@@(protected) @@
   persistenceContext = StatefulPersistenceContext.deserialize( ois, this );
   actionQueue = ActionQueue.deserialize( ois, this );

-    enabledFilters = ( Map ) ois.readObject();
+    loadQueryInfluencers = ( LoadQueryInfluencers ) ois.readObject();
+
   childSessionsByEntityMode = ( Map ) ois.readObject();

-    Iterator iter = enabledFilters.values().iterator();
+    Iterator iter = loadQueryInfluencers.getEnabledFilters().values().iterator();
   while ( iter.hasNext() ) {
-      ( ( FilterImpl ) iter.next() ).afterDeserialize(factory);
+      ( ( FilterImpl ) iter.next() ).afterDeserialize( factory );
   }

   if ( isRootSession && childSessionsByEntityMode != null ) {
@@(protected) @@
   oos.writeObject( cacheMode.toString() );
   oos.writeBoolean( flushBeforeCompletionEnabled );
   oos.writeBoolean( autoCloseSessionEnabled );
-    oos.writeObject( fetchProfile );
   // we need to writeObject() on this since interceptor is user defined
   oos.writeObject( interceptor );

@@(protected) @@
   actionQueue.serialize( oos );

   // todo : look at optimizing these...
-    oos.writeObject( enabledFilters );
+    oos.writeObject( loadQueryInfluencers );
   oos.writeObject( childSessionsByEntityMode );
 }
}

Modified: core/trunk/core/src/main/java/org/hibernate/impl/StatelessSessionImpl.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/impl/StatelessSessionImpl.java  2008-08-15 21:03:38 UTC (rev 15090)
+++ core/trunk/core/src/main/java/org/hibernate/impl/StatelessSessionImpl.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@
import org.hibernate.engine.QueryParameters;
import org.hibernate.engine.StatefulPersistenceContext;
import org.hibernate.engine.Versioning;
+import org.hibernate.engine.LoadQueryInfluencers;
import org.hibernate.engine.query.HQLQueryPlan;
import org.hibernate.engine.query.NativeSQLQueryPlan;
import org.hibernate.engine.query.sql.NativeSQLQuerySpecification;
@@(protected) @@
   errorIfClosed();
   String entityName = criteria.getEntityOrClassName();
   CriteriaLoader loader = new CriteriaLoader(
-        getOuterJoinLoadable(entityName),
+        getOuterJoinLoadable( entityName ),
        factory,
        criteria,
        entityName,
-         getEnabledFilters()
-      );
+         getLoadQueryInfluencers()
+    );
   return loader.scroll(this, scrollMode);
 }

@@(protected) @@
          factory,
          criteria,
          implementors[i],
-           getEnabledFilters()
+           getLoadQueryInfluencers()
     );
   }

@@(protected) @@
   return jdbcContext;
 }

+  public LoadQueryInfluencers getLoadQueryInfluencers() {
+    return null;
+  }
+
 public void setFetchProfile(String name) {}

 public void afterTransactionBegin(Transaction tx) {}

Modified: core/trunk/core/src/main/java/org/hibernate/loader/AbstractEntityJoinWalker.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/AbstractEntityJoinWalker.java  2008-08-15 21:03:38 UTC (rev 15090)
+++ core/trunk/core/src/main/java/org/hibernate/loader/AbstractEntityJoinWalker.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@

import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
+import java.util.Iterator;

import org.hibernate.FetchMode;
import org.hibernate.LockMode;
import org.hibernate.MappingException;
import org.hibernate.engine.CascadeStyle;
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.entity.Loadable;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.sql.JoinFragment;
@@(protected) @@
 private final OuterJoinLoadable persister;
 private final String alias;

-  public AbstractEntityJoinWalker(OuterJoinLoadable persister, SessionFactoryImplementor factory, Map enabledFilters) {
-    this( persister, factory, enabledFilters, null );
+  public AbstractEntityJoinWalker(
+      OuterJoinLoadable persister,
+      SessionFactoryImplementor factory,
+      LoadQueryInfluencers loadQueryInfluencers) {
+    this( persister, factory, loadQueryInfluencers, null );
 }

-  public AbstractEntityJoinWalker(OuterJoinLoadable persister, SessionFactoryImplementor factory, Map enabledFilters, String alias) {
-    super( factory, enabledFilters );
+  public AbstractEntityJoinWalker(
+      OuterJoinLoadable persister,
+      SessionFactoryImplementor factory,
+      LoadQueryInfluencers loadQueryInfluencers,
+      String alias) {
+    super( factory, loadQueryInfluencers );
   this.persister = persister;
   this.alias = ( alias == null ) ? generateRootAlias( persister.getEntityName() ) : alias;
 }

 protected final void initAll(
-    final String whereString,
-    final String orderByString,
-    final LockMode lockMode)
-  throws MappingException {
+      final String whereString,
+      final String orderByString,
+      final LockMode lockMode) 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,
-        getFactory(),
-        CollectionHelper.EMPTY_MAP
-      ) );
-
+    allAssociations.add(
+        new OuterJoinableAssociation(
+            persister.getEntityType(),
+            null,
+            null,
+            alias,
+            JoinFragment.LEFT_OUTER_JOIN,
+            getFactory(),
+            CollectionHelper.EMPTY_MAP
+        )
+    );
   initPersisters(allAssociations, lockMode);
   initStatementString( whereString, orderByString, lockMode);
 }

 protected final void initProjection(
-    final String projectionString,
-    final String whereString,
-    final String orderByString,
-    final String groupByString,
-    final LockMode lockMode)
-  throws MappingException {
+      final String projectionString,
+      final String whereString,
+      final String orderByString,
+      final String groupByString,
+      final LockMode lockMode) throws MappingException {
   walkEntityTree( persister, getAlias() );
   persisters = new Loadable[0];
   initStatementString(projectionString, whereString, orderByString, groupByString, lockMode);
 }

 private void initStatementString(
-    final String condition,
-    final String orderBy,
-    final LockMode lockMode)
-  throws MappingException {
+      final String condition,
+      final String orderBy,
+      final LockMode lockMode) throws MappingException {
   initStatementString(null, condition, orderBy, "", lockMode);
 }

@@(protected) @@
  * The superclass deliberately excludes collections
  */
 protected boolean isJoinedFetchEnabled(AssociationType type, FetchMode config, CascadeStyle cascadeStyle) {
-    return isJoinedFetchEnabledInMapping(config, type);
+    return isJoinedFetchEnabledInMapping( config, type );
 }

+  protected final boolean isJoinFetchEnabledByProfile(OuterJoinLoadable persister, String path, int propertyNumber) {
+    if ( !getLoadQueryInfluencers().hasEnabledFetchProfiles() ) {
+      // perf optimization
+      return false;
+    }
+
+    // ugh, this stuff has to be made easier...
+    String rootPropertyName = persister.getSubclassPropertyName( propertyNumber );
+    int pos = path.lastIndexOf( rootPropertyName );
+    String relativePropertyPath = pos >= 0
+        ? path.substring( pos )
+        : rootPropertyName;
+    String fetchRole = persister.getEntityName() + "." + relativePropertyPath;
+
+    Iterator profiles = getLoadQueryInfluencers().getEnabledFetchProfileNames().iterator();
+    while ( profiles.hasNext() ) {
+      final String profileName = ( String ) profiles.next();
+      final FetchProfile profile = getFactory().getFetchProfile( profileName );
+      final Fetch fetch = profile.getFetchByRole( fetchRole );
+      if ( fetch != null && Fetch.Style.JOIN == fetch.getStyle() ) {
+        return true;
+      }
+    }
+    return false;
+  }
+
 public abstract String getComment();

 protected final Loadable getPersister() {

Modified: core/trunk/core/src/main/java/org/hibernate/loader/JoinWalker.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/JoinWalker.java  2008-08-15 21:03:38 UTC (rev 15090)
+++ core/trunk/core/src/main/java/org/hibernate/loader/JoinWalker.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
import java.util.Set;

import org.hibernate.FetchMode;
@@(protected) @@
import org.hibernate.engine.CascadeStyle;
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;
@@(protected) @@
 private final SessionFactoryImplementor factory;
 protected final List associations = new ArrayList();
 private final Set visitedAssociationKeys = new HashSet();
-  private final Map enabledFilters;
+  private final LoadQueryInfluencers loadQueryInfluencers;

 protected String[] suffixes;
 protected String[] collectionSuffixes;
@@(protected) @@
 protected String[] aliases;
 protected LockMode[] lockModeArray;
 protected String sql;
+
+  protected JoinWalker(
+      SessionFactoryImplementor factory,
+      LoadQueryInfluencers loadQueryInfluencers) {
+    this.factory = factory;
+    this.loadQueryInfluencers = loadQueryInfluencers;
+
+  }
 
 public String[] getCollectionSuffixes() {
   return collectionSuffixes;
@@(protected) @@
 protected Dialect getDialect() {
   return factory.getDialect();
 }
-  
-  protected Map getEnabledFilters() {
-    return enabledFilters;
-  }

-  protected JoinWalker(SessionFactoryImplementor factory, Map enabledFilters) {
-    this.factory = factory;
-    this.enabledFilters = enabledFilters;
+  public LoadQueryInfluencers getLoadQueryInfluencers() {
+    return loadQueryInfluencers;
 }

 /**
@@(protected) @@
  * of associations to be fetched by outerjoin (if necessary)
  */
 private void addAssociationToJoinTreeIfNecessary(
-    final AssociationType type,
-    final String[] aliasedLhsColumns,
-    final String alias,
-    final String path,
-    int currentDepth,
-    final int joinType)
-  throws MappingException {
-    
-    if (joinType>=0) {  
+      final AssociationType type,
+      final String[] aliasedLhsColumns,
+      final String alias,
+      final String path,
+      int currentDepth,
+      final int joinType) throws MappingException {
+    if ( joinType >= 0 ) {
     addAssociationToJoinTree(
         type,
         aliasedLhsColumns,
@@(protected) @@
         path,
         currentDepth,
         joinType
-        );
+      );
   }
-
 }

 /**
@@(protected) @@
  * of associations to be fetched by outerjoin
  */
 private void addAssociationToJoinTree(
-    final AssociationType type,
-    final String[] aliasedLhsColumns,
-    final String alias,
-    final String path,
-    final int currentDepth,
-    final int joinType)
-  throws MappingException {
+      final AssociationType type,
+      final String[] aliasedLhsColumns,
+      final String alias,
+      String path,
+      final int currentDepth,
+      final int joinType) throws MappingException {

   Joinable joinable = type.getAssociatedJoinable( getFactory() );

-    String subalias = generateTableAlias(
-        associations.size()+1, //before adding to collection!
-        path,
-        joinable
-      );
+    // important to generate alias based on size of association collection
+    // *before* adding this join to that collection
+    String subalias = generateTableAlias( associations.size() + 1, path, joinable );

+    // NOTE : it should be fine to continue to pass only filters below
+    // (instead of LoadQueryInfluencers) since "from that point on" we
+    // only need to worry about restrictions (and not say adding more
+    // joins)
   OuterJoinableAssociation assoc = new OuterJoinableAssociation(
       type,
       alias,
       aliasedLhsColumns,
       subalias,
       joinType,
-        getFactory(),
-        enabledFilters
-      );
-    assoc.validateJoin(path);
-    associations.add(assoc);
+        getFactory(),
+        loadQueryInfluencers.getEnabledFilters()
+    );
+    assoc.validateJoin( path );
+    associations.add( assoc );

-    int nextDepth = currentDepth+1;
+    int nextDepth = currentDepth + 1;
+//    path = "";
   if ( !joinable.isCollection() ) {
     if (joinable instanceof OuterJoinLoadable) {
       walkEntityTree(
@@(protected) @@
 }

 /**
-   * For an entity class, return a list of associations to be fetched by outerjoin
+   * Walk the association tree for an entity, adding associations which should
+   * be join fetched to the {@(protected)
+   * entry point into the walking for a given entity, starting the recursive
+   * calls into {@(protected))}.
+   *
+   * @param persister The persister representing the entity to be walked.
+   * @param alias The (root) alias to use for this entity/persister.
+   * @throws org.hibernate.MappingException ???
  */
-  protected final void walkEntityTree(OuterJoinLoadable persister, String alias)
-  throws MappingException {
-    walkEntityTree(persister, alias, "", 0);
+  protected final void walkEntityTree(
+      OuterJoinLoadable persister,
+      String alias) throws MappingException {
+    walkEntityTree( persister, alias, "", 0 );
 }

 /**
@@(protected) @@
 }
 
 /**
-   * Walk the tree for a particular entity association
+   * Process a particular association owned by the entity
+   *
+   * @param associationType The type representing the association to be
+   * processed.
+   * @param persister The owner of the association to be processed.
+   * @param propertyNumber The property number for the association
+   * (relative to the persister).
+   * @param alias The entity alias
+   * @param path The path to the association
+   * @param nullable is the association nullable (which I think is supposed
+   * to indicate inner/outer join semantics).
+   * @param currentDepth The current join depth
+   * @throws org.hibernate.MappingException ???
  */
-  private final void walkEntityAssociationTree(
-    final AssociationType associationType,
-    final OuterJoinLoadable persister,
-    final int propertyNumber,
-    final String alias,
-    final String path,
-    final boolean nullable,
-    final int currentDepth)
-  throws MappingException {
-
+  private void walkEntityAssociationTree(
+      final AssociationType associationType,
+      final OuterJoinLoadable persister,
+      final int propertyNumber,
+      final String alias,
+      final String path,
+      final boolean nullable,
+      final int currentDepth) throws MappingException {
   String[] aliasedLhsColumns = JoinHelper.getAliasedLHSColumnNames(
       associationType, alias, propertyNumber, persister, getFactory()
-      );
-
+    );
   String[] lhsColumns = JoinHelper.getLHSColumnNames(
       associationType, propertyNumber, persister, getFactory()
-      );
+    );
   String lhsTable = JoinHelper.getLHSTableName(associationType, propertyNumber, persister);

   String subpath = subPath( path, persister.getSubclassPropertyName(propertyNumber) );
   int joinType = getJoinType(
+        persister,
+        subpath,
+        propertyNumber,
       associationType,
-        persister.getFetchMode(propertyNumber),
-        subpath,
+        persister.getFetchMode( propertyNumber ),
+        persister.getCascadeStyle( propertyNumber ),
       lhsTable,
       lhsColumns,
       nullable,
-        currentDepth,
-        persister.getCascadeStyle(propertyNumber)
-      );
+        currentDepth
+    );
   addAssociationToJoinTreeIfNecessary(
       associationType,
       aliasedLhsColumns,
@@(protected) @@
       subpath,
       currentDepth,
       joinType
-      );
+    );
+  }

+  /**
+   * Determine the appropriate type of join (if any) to use to fetch the
+   * given association.
+   *
+   * @param persister The owner of the association.
+   * @param path The path to the association
+   * @param propertyNumber The property number representing the association.
+   * @param associationType The association type.
+   * @param metadataFetchMode The metadata-defined fetch mode.
+   * @param metadataCascadeStyle The metadata-defined cascade style.
+   * @param lhsTable The owner table
+   * @param lhsColumns The owner join columns
+   * @param nullable Is the association nullable.
+   * @param currentDepth Current join depth
+   * @return type of join to use ({@(protected)},
+   * {@(protected).
+   * @throws MappingException ??
+   */
+  protected int getJoinType(
+      OuterJoinLoadable persister,
+      final String path,
+      int propertyNumber,
+      AssociationType associationType,
+      FetchMode metadataFetchMode,
+      CascadeStyle metadataCascadeStyle,
+      String lhsTable,
+      String[] lhsColumns,
+      final boolean nullable,
+      final int currentDepth) throws MappingException {
+    return getJoinType(
+        associationType,
+        metadataFetchMode,
+        path,
+        lhsTable,
+        lhsColumns,
+        nullable,
+        currentDepth,
+        metadataCascadeStyle
+    );
 }

 /**
-   * For an entity class, add to a list of associations to be fetched
-   * by outerjoin
+   * Determine the appropriate associationType of join (if any) to use to fetch the
+   * given association.
+   *
+   * @param associationType The association associationType.
+   * @param config The metadata-defined fetch mode.
+   * @param path The path to the association
+   * @param lhsTable The owner table
+   * @param lhsColumns The owner join columns
+   * @param nullable Is the association nullable.
+   * @param currentDepth Current join depth
+   * @param cascadeStyle The metadata-defined cascade style.
+   * @return type of join to use ({@(protected)},
+   * {@(protected).
+   * @throws MappingException ??
  */
-  private final void walkEntityTree(
-    final OuterJoinLoadable persister,
-    final String alias,
-    final String path,
-    final int currentDepth)
-  throws MappingException {
+  private int getJoinType(
+      AssociationType associationType,
+      FetchMode config,
+      String path,
+      String lhsTable,
+      String[] lhsColumns,
+      boolean nullable,
+      int currentDepth,
+      CascadeStyle cascadeStyle) throws MappingException {
+    if ( !isJoinedFetchEnabled( associationType, config, cascadeStyle ) ) {
+      return -1;
+    }
+    if ( isTooDeep(currentDepth) || ( associationType.isCollectionType() && isTooManyCollections() ) ) {
+      return -1;
+    }
+    if ( isDuplicateAssociation( lhsTable, lhsColumns, associationType ) ) {
+      return -1;
+    }
+    return getJoinType( nullable, currentDepth );
+  }

+  /**
+   * Walk the association tree for an entity, adding associations which should
+   * be join fetched to the {@(protected)
+   * entry point into the walking for a given entity, starting the recursive
+   * calls into {@(protected))}.
+   *
+   * @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 currentDepth The current join depth
+   * @throws org.hibernate.MappingException ???
+   */
+  private void walkEntityTree(
+      final OuterJoinLoadable persister,
+      final String alias,
+      final String path,
+      final int currentDepth) throws MappingException {
   int n = persister.countSubclassProperties();
-    for ( int i=0; i<n; i++ ) {
+    for ( int i = 0; i < n; i++ ) {
     Type type = persister.getSubclassPropertyType(i);
     if ( type.isAssociationType() ) {
       walkEntityAssociationTree(
-          (AssociationType) type,
+          ( AssociationType ) type,
         persister,
         i,
         alias,
@@(protected) @@
     }
     else if ( type.isComponentType() ) {
       walkComponentTree(
-          (AbstractComponentType) type,
+          ( AbstractComponentType ) type,
         i,
         0,
         persister,
@@(protected) @@

 /**
  * For a component, add to a list of associations to be fetched by outerjoin
+   *
+   *
+   * @param componentType The component type to be walked.
+   * @param propertyNumber The property number for the component property (relative to
+   * persister).
+   * @param begin todo unknowm
+   * @param persister The owner of the component property
+   * @param alias The root alias
+   * @param path The property access path
+   * @param currentDepth The current join depth
+   * @throws org.hibernate.MappingException ???
  */
 private void walkComponentTree(
-    final AbstractComponentType componentType,
-    final int propertyNumber,
-    int begin,
-    final OuterJoinLoadable persister,
-    final String alias,
-    final String path,
-    final int currentDepth
-  ) throws MappingException {
-
+      final AbstractComponentType componentType,
+      final int propertyNumber,
+      int begin,
+      final OuterJoinLoadable persister,
+      final String alias,
+      final String path,
+      final int currentDepth) throws MappingException {
   Type[] types = componentType.getSubtypes();
   String[] propertyNames = componentType.getPropertyNames();
-    for ( int i=0; i <types.length; i++ ) {
-
+    for ( int i = 0; i < types.length; i++ ) {
     if ( types[i].isAssociationType() ) {
       AssociationType associationType = (AssociationType) types[i];
-
       String[] aliasedLhsColumns = JoinHelper.getAliasedLHSColumnNames(
         associationType, alias, propertyNumber, begin, persister, getFactory()
       );
-
       String[] lhsColumns = JoinHelper.getLHSColumnNames(
         associationType, propertyNumber, begin, persister, getFactory()
       );
@@(protected) @@
       String subpath = subPath( path, propertyNames[i] );
       final boolean[] propertyNullability = componentType.getPropertyNullability();
       final int joinType = getJoinType(
+            persister,
+            subpath,
+            propertyNumber,
           associationType,
           componentType.getFetchMode(i),
-            subpath,
+            componentType.getCascadeStyle(i),
           lhsTable,
           lhsColumns,
           propertyNullability==null || propertyNullability[i],
-            currentDepth,
-            componentType.getCascadeStyle(i)
-          );
+            currentDepth
+        );
       addAssociationToJoinTreeIfNecessary(      
           associationType,
           aliasedLhsColumns,
@@(protected) @@
           subpath,
           currentDepth,
           joinType
-          );
+        );

     }
     else if ( types[i].isComponentType() ) {
       String subpath = subPath( path, propertyNames[i] );
       walkComponentTree(
-            (AbstractComponentType) types[i],
+            ( AbstractComponentType ) types[i],
           propertyNumber,
           begin,
           persister,
           alias,
           subpath,
           currentDepth
-          );
+        );
     }
-      
-      begin+=types[i].getColumnSpan( getFactory() );
+      begin += types[i].getColumnSpan( getFactory() );
   }

 }
@@(protected) @@
  * For a composite element, add to a list of associations to be fetched by outerjoin
  */
 private void walkCompositeElementTree(
-    final AbstractComponentType compositeType,
-    final String[] cols,
-    final QueryableCollection persister,
-    final String alias,
-    final String path,
-    final int currentDepth)
-  throws MappingException {
+      final AbstractComponentType compositeType,
+      final String[] cols,
+      final QueryableCollection persister,
+      final String alias,
+      final String path,
+      final int currentDepth) throws MappingException {

   Type[] types = compositeType.getSubtypes();
   String[] propertyNames = compositeType.getPropertyNames();
@@(protected) @@
 }

 /**
-   * Get the join type (inner, outer, etc) or -1 if the
-   * association should not be joined. Override on
-   * subclasses.
-   */
-  protected int getJoinType(
-      AssociationType type,
-      FetchMode config,
-      String path,
-      String lhsTable,
-      String[] lhsColumns,
-      boolean nullable,
-      int currentDepth,
-      CascadeStyle cascadeStyle)
-  throws MappingException {
-    
-    if ( !isJoinedFetchEnabled(type, config, cascadeStyle) ) return -1;
-    
-    if ( isTooDeep(currentDepth) || ( type.isCollectionType() && isTooManyCollections() ) ) return -1;
-    
-    final boolean dupe = isDuplicateAssociation(lhsTable, lhsColumns, type);
-    if (dupe) return -1;
-    
-    return getJoinType(nullable, currentDepth);
-    
-  }
-  
-  /**
  * Use an inner join if it is a non-null association and this
  * is the "first" join in a series
  */
 protected int getJoinType(boolean nullable, int currentDepth) {
   //TODO: this is too conservative; if all preceding joins were
   //    also inner joins, we could use an inner join here
-    return !nullable && currentDepth==0 ?
-          JoinFragment.INNER_JOIN :
-          JoinFragment.LEFT_OUTER_JOIN;
+    return !nullable && currentDepth == 0
+        ? JoinFragment.INNER_JOIN
+        : JoinFragment.LEFT_OUTER_JOIN;
 }

 protected boolean isTooDeep(int currentDepth) {
@@(protected) @@
 protected String generateTableAlias(
     final int n,
     final String path,
-      final Joinable joinable
-  ) {
+      final Joinable joinable) {
   return StringHelper.generateAlias( joinable.getName(), n );
 }


Modified: core/trunk/core/src/main/java/org/hibernate/loader/OuterJoinLoader.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/OuterJoinLoader.java  2008-08-15 21:03:38 UTC (rev 15090)
+++ core/trunk/core/src/main/java/org/hibernate/loader/OuterJoinLoader.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@
package org.hibernate.loader;

import java.util.Map;
+import java.util.Set;

import org.hibernate.LockMode;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.LoadQueryInfluencers;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.type.EntityType;
@@(protected) @@
 protected String[] suffixes;
 protected String[] collectionSuffixes;

-   private Map enabledFilters;
-  
-   protected final Dialect getDialect() {
+   private LoadQueryInfluencers loadQueryInfluencers;
+
+  protected final Dialect getDialect() {
   return getFactory().getDialect();
  }

-  public OuterJoinLoader(SessionFactoryImplementor factory, Map enabledFilters) {
-    super(factory);
-    this.enabledFilters = enabledFilters;
+  public OuterJoinLoader(
+      SessionFactoryImplementor factory,
+      LoadQueryInfluencers loadQueryInfluencers) {
+    super( factory );
+    this.loadQueryInfluencers = loadQueryInfluencers;
 }

 protected String[] getSuffixes() {
@@(protected) @@
 protected LockMode[] getLockModes(Map lockModes) {
   return lockModeArray;
 }
-  
-  public Map getEnabledFilters() {
-    return enabledFilters;
+
+  public LoadQueryInfluencers getLoadQueryInfluencers() {
+    return loadQueryInfluencers;
 }

 protected final String[] getAliases() {

Modified: core/trunk/core/src/main/java/org/hibernate/loader/OuterJoinableAssociation.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/OuterJoinableAssociation.java  2008-08-15 21:03:38 UTC (rev 15090)
+++ core/trunk/core/src/main/java/org/hibernate/loader/OuterJoinableAssociation.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@
import org.hibernate.type.AssociationType;
import org.hibernate.type.EntityType;

+/**
+ * Part of the Hibernate SQL rendering internals. This class represents
+ * a joinable association.
+ *
+ * @author Gavin King
+ */
public final class OuterJoinableAssociation {
 private final AssociationType joinableType;
 private final Joinable joinable;
@@(protected) @@
 private final Map enabledFilters;

 public OuterJoinableAssociation(
-    AssociationType joinableType,
-    String lhsAlias,
-    String[] lhsColumns,
-    String rhsAlias,
-    int joinType,
-    SessionFactoryImplementor factory,
-    Map enabledFilters)
-  throws MappingException {
+      AssociationType joinableType,
+      String lhsAlias,
+      String[] lhsColumns,
+      String rhsAlias,
+      int joinType,
+      SessionFactoryImplementor factory,
+      Map enabledFilters) throws MappingException {
   this.joinableType = joinableType;
   this.lhsAlias = lhsAlias;
   this.lhsColumns = lhsColumns;

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  2008-08-15 21:03:38 UTC (rev 15090)
+++ core/trunk/core/src/main/java/org/hibernate/loader/collection/BasicCollectionJoinWalker.java  2008-08-15 21:20:15 UTC (rev 15091)
@@(protected) @@
import org.hibernate.LockMode;
import org.hibernate.MappingException;
import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.engine.LoadQueryInfluencers;
+import org.hibernate.engine.CascadeStyle;
import org.hibernate.loader.BasicLoader;
import org.hibernate.loader.OuterJoinableAssociation;
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;
@@(protected) @@
     int batchSize,
     String subquery,
     SessionFactoryImplementor factory,
-      Map enabledFilters)
-  throws MappingException {
+      LoadQueryInfluencers loadQueryInfluencers) throws MappingException {

-    super(factory, enabledFilters);
+    super( factory, loadQueryInfluencers );

   this.collectionPersister = collectionPersister;

@@(protected) @@

   List allAssociations = new ArrayList();
   allAssociations.addAll(associations);
-    allAssociations.add( new OuterJoinableAssociation(
-        collectionPersister.getCollectionType(),
-        null,
-        null,
-        alias,
-        JoinFragment.LEFT_OUTER_JOIN,
-        getFactory(),
-        CollectionHelper.EMPTY_MAP
-      ) );
-
+    allAssociations.add(
+        new OuterJoinableAssociation(
+            collectionPersister.getCollectionType(),
+            null,
+            null,
+            alias,
+            JoinFragment.LEFT_OUTER_JOIN,
+            getFactory(),
+            CollectionHelper.EMPTY_MAP
+        )
+    );
   initPersisters(allAssociations, LockMode.NONE);
   initStatementString(alias, batchSize, subquery);
-
 }

 private void initStatementString(
   final String alias,
   final int batchSize,
-    final String subquery)
-  throws MappingException {
+    final String subquery) throws MappingException {

   final int joins = countEntityPersisters( associations );
   final int collectionJoins = countCollectionPersisters( associations ) + 1;
@@(protected) @@
     );

   String manyToManyOrderBy = "";
-    String filter = collectionPersister.filterFragment( alias, getEnabledFilters() );
+    String filter = collectionPersister.filterFragment( alias, getLoadQueryInfluencers().getEnabledFilters() );
   if ( collectionPersister.isManyToMany() ) {
     // from the collection of associations, locate OJA for the
     // ManyToOne corresponding to this persister to fully
@@(protected) @@
         // we found it
         filter += collectionPersister.getManyToManyFilterFragment(
             oja.getRHSAlias(),
-              getEnabledFilters()
+              getLoadQueryInfluencers().getEnabledFilters()
           );
-            manyToManyOrderBy += collectionPersister.getManyToManyOrderByString( oja.getRHSAlias() );
+          manyToManyOrderBy += collectionPersister.getManyToManyOrderByString( oja.getRHSAlias() );
       }
     }
   }
@@(protected) @@
   sql = select.toStatementString();
 }

-  /**
-   * We can use an inner join for first many-to-many association
-   */
 protected int getJoinType(
-      AssociationType type,
-      FetchMode config,
-      String path,
-      Set visitedAssociations,
+      OuterJoinLoadable persister,
+      String path,
+      int propertyNumber,
+      AssociationType associationType,
+      FetchMode metadataFetchMode,
+      CascadeStyle metadataCascadeStyle,
     String lhsTable,
     String[] lhsColumns,
     boolean nullable,
-      int currentDepth)
-  throws MappingException {
-
+      int currentDepth) throws MappingException {
   int joinType = super.getJoinType(
-        type,
-        config,
-        path,
-        lhsTable,
-        lhsColumns,
-        nullable,
-        currentDepth,
-        null
-      );
+        persister,
+ &n