[hibernate-commits] Hibernate SVN: r10615 - in trunk/Hibernate3: src/org/hibernate/action src/org/hibernate/hql/ast src/org/hibernate/loader src/org/hibernate/loader/hql src/org/hibernate/param test/org/hibernate/test/hql

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Wed Oct 18 23:27:07 EDT 2006


Author: steve.ebersole at jboss.com
Date: 2006-10-18 23:27:04 -0400 (Wed, 18 Oct 2006)
New Revision: 10615

Added:
   trunk/Hibernate3/src/org/hibernate/param/CollectionFilterKeyParameterSpecification.java
   trunk/Hibernate3/src/org/hibernate/param/DynamicFilterParameterSpecification.java
Modified:
   trunk/Hibernate3/src/org/hibernate/action/EntityUpdateAction.java
   trunk/Hibernate3/src/org/hibernate/hql/ast/HqlSqlWalker.java
   trunk/Hibernate3/src/org/hibernate/hql/ast/ParameterTranslationsImpl.java
   trunk/Hibernate3/src/org/hibernate/loader/Loader.java
   trunk/Hibernate3/src/org/hibernate/loader/hql/QueryLoader.java
   trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java
Log:
HHH-1774 : proper binding of multi-column parameter types to queries

Modified: trunk/Hibernate3/src/org/hibernate/action/EntityUpdateAction.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/action/EntityUpdateAction.java	2006-10-19 03:25:41 UTC (rev 10614)
+++ trunk/Hibernate3/src/org/hibernate/action/EntityUpdateAction.java	2006-10-19 03:27:04 UTC (rev 10615)
@@ -81,7 +81,7 @@
 					persister.getRootEntityName(), 
 					session.getEntityMode(), 
 					session.getFactory() 
-				);
+			);
 			lock = persister.getCache().lock(ck, previousVersion);
 		}
 		else {
@@ -99,41 +99,35 @@
 					instance, 
 					rowId, 
 					session 
-				);
+			);
 		}
 
-
 		EntityEntry entry = getSession().getPersistenceContext().getEntry( instance );
 		if ( entry == null ) {
 			throw new AssertionFailure( "possible nonthreadsafe access to session" );
 		}
 		
 		if ( entry.getStatus()==Status.MANAGED || persister.isVersionPropertyGenerated() ) {
-
-			// get the updated snapshot by cloning current state
-			// it is safe to copy in place, since by this time
-			// no-one else has a reference to the array
-			TypeFactory.deepCopy( 
-					state, 
-					persister.getPropertyTypes(), 
-					persister.getPropertyCheckability(), 
-					state, 
-					session 
-				);
-			
 			if ( persister.hasUpdateGeneratedProperties() ) {
+				// get the updated snapshot by cloning current state;
+				// it is safe to copy in place, since by this time
+				// no-one else has a reference to the array
+				TypeFactory.deepCopy(
+						state,
+						persister.getPropertyTypes(),
+						persister.getPropertyCheckability(),
+						state,
+						session
+				);
 				persister.processUpdateGeneratedProperties( id, instance, state, session );
 				if ( persister.isVersionPropertyGenerated() ) {
 					nextVersion = Versioning.getVersion(state, persister);
 				}
 			}
-			
 			entry.postUpdate( instance, state, nextVersion );
-			
 		}
 
 		if ( persister.hasCache() ) {
-			
 			if ( persister.isCacheInvalidationRequired() || entry.getStatus()!=Status.MANAGED ) {
 				persister.getCache().evict(ck);
 			}
@@ -146,17 +140,14 @@
 						nextVersion,
 						getSession(),
 						instance
-					);
-				cacheEntry = persister.getCacheEntryStructure().structure(ce);
-//				boolean put = persister.getCache().update(ck, cacheEntry);
+				);
+				cacheEntry = persister.getCacheEntryStructure().structure( ce );
 				boolean put = persister.getCache().update( ck, cacheEntry, nextVersion, previousVersion );
-				
 				if ( put && factory.getStatistics().isStatisticsEnabled() ) {
 					factory.getStatisticsImplementor()
 							.secondLevelCachePut( getPersister().getCache().getRegionName() );
 				}
 			}
-			
 		}
 
 		postUpdate();

Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/HqlSqlWalker.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/hql/ast/HqlSqlWalker.java	2006-10-19 03:25:41 UTC (rev 10614)
+++ trunk/Hibernate3/src/org/hibernate/hql/ast/HqlSqlWalker.java	2006-10-19 03:27:04 UTC (rev 10615)
@@ -60,6 +60,7 @@
 import org.hibernate.param.ParameterSpecification;
 import org.hibernate.param.PositionalParameterSpecification;
 import org.hibernate.param.VersionTypeSeedParameterSpecification;
+import org.hibernate.param.CollectionFilterKeyParameterSpecification;
 import org.hibernate.persister.collection.QueryableCollection;
 import org.hibernate.persister.entity.Queryable;
 import org.hibernate.sql.JoinFragment;
@@ -87,98 +88,50 @@
  * @see SqlASTFactory
  */
 public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, ParameterBinder.NamedParameterSource {
-	private static Log log = LogFactory.getLog( HqlSqlWalker.class );
+	private static final Log log = LogFactory.getLog( HqlSqlWalker.class );
 
-	/**
-	 * A delegate that handles the Hiberanate meta data model.
-	 */
-	private SessionFactoryHelper sessionFactoryHelper;
+	private final QueryTranslatorImpl queryTranslatorImpl;
+	private final HqlParser hqlParser;
+	private final SessionFactoryHelper sessionFactoryHelper;
+	private final Map tokenReplacements;
+	private final AliasGenerator aliasGenerator = new AliasGenerator();
+	private final LiteralProcessor literalProcessor;
+	private final ParseErrorHandler parseErrorHandler;
+	private final ASTPrinter printer;
+	private final String collectionFilterRole;
 
-	/**
-	 * A delegate that handles literal constants.
-	 */
-	private LiteralProcessor literalProcessor;
-
-	/**
-	 * Handles parser errors.
-	 */
-	private ParseErrorHandler parseErrorHandler;
-
-	/**
-	 * The current context.
-	 */
 	private FromClause currentFromClause = null;
-
-	/**
-	 * The top-level SelectClause.
-	 */
 	private SelectClause selectClause;
 
-	/**
-	 * Generates alias names for tables.
-	 */
-	private AliasGenerator aliasGenerator = new AliasGenerator();
-
-	/**
-	 * The set of unique query spaces (a.k.a. table names).
-	 */
 	private Set querySpaces = new HashSet();
 
-	/**
-	 * A (string->string) map is used to substitute function names and literals.
-	 */
-	private Map tokenReplacements;
-
-	private QueryTranslatorImpl queryTranslatorImpl;
-
-	/**
-	 * The number of parameters encountered so far.
-	 */
 	private int parameterCount;
-
-	/**
-	 * A map of lists which associates named and numbered parameters to a list of occurrences in the query.
-	 */
 	private Map namedParameters = new HashMap();
-
-	/**
-	 * The filter collection role, or null if this isn't a filter compilation.
-	 */
-	private String filterCollectionRole;
-
-	/**
-	 * The parser that performed phase 1 - parse the HQL into an HQL tree.
-	 */
-	private HqlParser hqlParser;
-
-	private ASTPrinter printer;
-
-	/**
-	 * The join type for any implied joins.
-	 */
-	private int impliedJoinType;
-
 	private ArrayList parameters = new ArrayList();
 	private int numberOfParametersInSetClause;
 	private int positionalParameterCount;
 
 	private ArrayList assignmentSpecifications = new ArrayList();
 
+	private int impliedJoinType;
+
 	/**
 	 * Create a new tree transformer.
 	 *
-	 * @param qti               Back pointer to the query translator implementation that is using this tree transform.
-	 * @param sfi               The session factory implementor where the Hibernate mappings can be found.
-	 * @param parser
+	 * @param qti Back pointer to the query translator implementation that is using this tree transform.
+	 * @param sfi The session factory implementor where the Hibernate mappings can be found.
+	 * @param parser A reference to the phase-1 parser
 	 * @param tokenReplacements Registers the token replacement map with the walker.  This map will
-	 *                          be used to substitute function names and constants.
-	 * @param collectionRole    the role name of the collection used as the basis for the filter, NULL if this
+	 * be used to substitute function names and constants.
+	 * @param collectionRole The collection role name of the collection used as the basis for the
+	 * filter, NULL if this is not a collection filter compilation.
 	 */
-	public HqlSqlWalker(QueryTranslatorImpl qti,
-						SessionFactoryImplementor sfi,
-						HqlParser parser,
-						Map tokenReplacements,
-						String collectionRole) {
+	public HqlSqlWalker(
+			QueryTranslatorImpl qti,
+			SessionFactoryImplementor sfi,
+			HqlParser parser,
+			Map tokenReplacements,
+			String collectionRole) {
 		setASTFactory( new SqlASTFactory( this ) );
 		// Initialize the error handling delegate.
 		this.parseErrorHandler = new ErrorCounter();
@@ -186,37 +139,73 @@
 		this.sessionFactoryHelper = new SessionFactoryHelper( sfi );
 		this.literalProcessor = new LiteralProcessor( this );
 		this.tokenReplacements = tokenReplacements;
-		this.filterCollectionRole = collectionRole;
+		this.collectionFilterRole = collectionRole;
 		this.hqlParser = parser;
 		this.printer = new ASTPrinter( SqlTokenTypes.class );
 	}
 
 
 	protected void prepareFromClauseInputTree(AST fromClauseInput) {
-		// Handle fiter compilation.
-		// IMPORTANT NOTE: This is modifying the INPUT (HQL) tree, not the output tree!
-		if ( isFilter() && !isSubQuery() ) {
-			QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( filterCollectionRole );
-			Type collectionElementType = persister.getElementType();
-			if ( !collectionElementType.isEntityType() ) {
-				throw new QueryException( "collection of values in filter: this" );
-			}
+		if ( !isSubQuery() ) {
+//			// inject param specifications to account for dynamic filter param values
+//			if ( ! getEnabledFilters().isEmpty() ) {
+//				Iterator filterItr = getEnabledFilters().values().iterator();
+//				while ( filterItr.hasNext() ) {
+//					FilterImpl filter = ( FilterImpl ) filterItr.next();
+//					if ( ! filter.getFilterDefinition().getParameterNames().isEmpty() ) {
+//						Iterator paramItr = filter.getFilterDefinition().getParameterNames().iterator();
+//						while ( paramItr.hasNext() ) {
+//							String parameterName = ( String ) paramItr.next();
+//							// currently param filters *only* work with single-column parameter types;
+//							// if that limitation is ever lifted, this logic will need to change to account for that
+//							ParameterNode collectionFilterKeyParameter = ( ParameterNode ) astFactory.create( PARAM, "?" );
+//							DynamicFilterParameterSpecification paramSpec = new DynamicFilterParameterSpecification(
+//									filter.getName(),
+//									parameterName,
+//									filter.getFilterDefinition().getParameterType( parameterName ),
+//									 positionalParameterCount++
+//							);
+//							collectionFilterKeyParameter.setHqlParameterSpecification( paramSpec );
+//							parameters.add( paramSpec );
+//						}
+//					}
+//				}
+//			}
 
-			String collectionElementEntityName = persister.getElementPersister().getEntityName();
-			ASTFactory inputAstFactory = hqlParser.getASTFactory();
-			AST fromElement = ASTUtil.create( inputAstFactory, HqlTokenTypes.FILTER_ENTITY, collectionElementEntityName );
-			ASTUtil.createSibling( inputAstFactory, HqlTokenTypes.ALIAS, "this", fromElement );
-			fromClauseInput.addChild( fromElement );
-			// Show the modified AST.
-			if ( log.isDebugEnabled() ) {
-				log.debug( "prepareFromClauseInputTree() : Filter - Added 'this' as a from element..." );
+			if ( isFilter() ) {
+				// Handle collection-fiter compilation.
+				// IMPORTANT NOTE: This is modifying the INPUT (HQL) tree, not the output tree!
+				QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( collectionFilterRole );
+				Type collectionElementType = persister.getElementType();
+				if ( !collectionElementType.isEntityType() ) {
+					throw new QueryException( "collection of values in filter: this" );
+				}
+
+				String collectionElementEntityName = persister.getElementPersister().getEntityName();
+				ASTFactory inputAstFactory = hqlParser.getASTFactory();
+				AST fromElement = ASTUtil.create( inputAstFactory, HqlTokenTypes.FILTER_ENTITY, collectionElementEntityName );
+				ASTUtil.createSibling( inputAstFactory, HqlTokenTypes.ALIAS, "this", fromElement );
+				fromClauseInput.addChild( fromElement );
+				// Show the modified AST.
+				if ( log.isDebugEnabled() ) {
+					log.debug( "prepareFromClauseInputTree() : Filter - Added 'this' as a from element..." );
+				}
+				queryTranslatorImpl.showHqlAst( hqlParser.getAST() );
+
+				// Create a parameter specification for the collection filter...
+				Type collectionFilterKeyType = sessionFactoryHelper.requireQueryableCollection( collectionFilterRole ).getKeyType();
+				ParameterNode collectionFilterKeyParameter = ( ParameterNode ) astFactory.create( PARAM, "?" );
+				CollectionFilterKeyParameterSpecification collectionFilterKeyParameterSpec = new CollectionFilterKeyParameterSpecification(
+						collectionFilterRole, collectionFilterKeyType, positionalParameterCount++
+				);
+				collectionFilterKeyParameter.setHqlParameterSpecification( collectionFilterKeyParameterSpec );
+				parameters.add( collectionFilterKeyParameterSpec );
 			}
-			queryTranslatorImpl.showHqlAst( hqlParser.getAST() );
 		}
 	}
 
-	private boolean isFilter() {
-		return filterCollectionRole != null;
+	public boolean isFilter() {
+		return collectionFilterRole != null;
 	}
 
 	public SessionFactoryHelper getSessionFactoryHelper() {
@@ -270,13 +259,13 @@
 	protected AST createFromFilterElement(AST filterEntity, AST alias) throws SemanticException {
 		FromElement fromElement = currentFromClause.addFromElement( filterEntity.getText(), alias );
 		FromClause fromClause = fromElement.getFromClause();
-		QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( filterCollectionRole );
+		QueryableCollection persister = sessionFactoryHelper.getCollectionPersister( collectionFilterRole );
 		// Get the names of the columns used to link between the collection
 		// owner and the collection elements.
 		String[] keyColumnNames = persister.getKeyColumnNames();
 		String fkTableAlias = persister.isOneToMany()
 				? fromElement.getTableAlias()
-				: fromClause.getAliasGenerator().createName( filterCollectionRole );
+				: fromClause.getAliasGenerator().createName( collectionFilterRole );
 		JoinSequence join = sessionFactoryHelper.createJoinSequence();
 		join.setRoot( persister, fkTableAlias );
 		if ( !persister.isOneToMany() ) {

Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/ParameterTranslationsImpl.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/hql/ast/ParameterTranslationsImpl.java	2006-10-19 03:25:41 UTC (rev 10614)
+++ trunk/Hibernate3/src/org/hibernate/hql/ast/ParameterTranslationsImpl.java	2006-10-19 03:27:04 UTC (rev 10615)
@@ -87,10 +87,7 @@
 			final ParameterSpecification spec = ( ParameterSpecification ) parameterSpecifications.get( i );
 			if ( PositionalParameterSpecification.class.isAssignableFrom( spec.getClass() ) ) {
 				PositionalParameterSpecification ordinalSpec = ( PositionalParameterSpecification ) spec;
-				ordinalParameterList.add(
-						ordinalSpec.getHqlPosition(),
-				        new ParameterInfo( i, ordinalSpec.getExpectedType() )
-				);
+				ordinalParameterList.add( new ParameterInfo( i, ordinalSpec.getExpectedType() ) );
 			}
 			else if ( NamedParameterSpecification.class.isAssignableFrom( spec.getClass() ) ) {
 				NamedParameterSpecification namedSpec = ( NamedParameterSpecification ) spec;

Modified: trunk/Hibernate3/src/org/hibernate/loader/Loader.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/loader/Loader.java	2006-10-19 03:25:41 UTC (rev 10614)
+++ trunk/Hibernate3/src/org/hibernate/loader/Loader.java	2006-10-19 03:27:04 UTC (rev 10615)
@@ -1497,26 +1497,6 @@
 	}
 
 	/**
-	 * Bind positional parameter values to the <tt>PreparedStatement</tt>
-	 * (these are parameters specified by a JDBC-style ?).
-	 */
-	protected int bindPositionalParameters(
-	        final PreparedStatement st,
-	        final QueryParameters queryParameters,
-	        final int start,
-	        final SessionImplementor session) throws SQLException, HibernateException {
-
-		final Object[] values = queryParameters.getFilteredPositionalParameterValues();
-		final Type[] types = queryParameters.getFilteredPositionalParameterTypes();
-		int span = 0;
-		for ( int i = 0; i < values.length; i++ ) {
-			types[i].nullSafeSet( st, values[i], start + span, session );
-			span += types[i].getColumnSpan( getFactory() );
-		}
-		return span;
-	}
-
-	/**
 	 * Obtain a <tt>PreparedStatement</tt> with all parameters pre-bound.
 	 * Bind JDBC-style <tt>?</tt> parameters, named parameters, and
 	 * limit parameters.
@@ -1572,9 +1552,9 @@
 			if (callable) {
 				col = dialect.registerResultSetOutParameter( (CallableStatement)st, col );
 			}
-			col += bindPositionalParameters( st, queryParameters, col, session );
-			col += bindNamedParameters( st, queryParameters.getNamedParameters(), col, session );
 
+			col += bindParameterValues( st, queryParameters, col, session );
+
 			if ( useLimit && !dialect.bindLimitParametersFirst() ) {
 				col += bindLimitParameters( st, col, selection );
 			}
@@ -1646,7 +1626,78 @@
 		}
 	}
 
+	protected int bindParameterValues(
+			PreparedStatement st,
+			QueryParameters queryParameters,
+			int col,
+			SessionImplementor session) throws SQLException {
+		col += bindPositionalParameters( st, queryParameters, col, session );
+		col += bindNamedParameters( st, queryParameters.getNamedParameters(), col, session );
+		return col;
+	}
+
 	/**
+	 * Bind positional parameter values to the <tt>PreparedStatement</tt>
+	 * (these are parameters specified by a JDBC-style ?).
+	 */
+	protected int bindPositionalParameters(
+	        final PreparedStatement st,
+	        final QueryParameters queryParameters,
+	        final int start,
+	        final SessionImplementor session) throws SQLException, HibernateException {
+		final Object[] values = queryParameters.getFilteredPositionalParameterValues();
+		final Type[] types = queryParameters.getFilteredPositionalParameterTypes();
+		int span = 0;
+		for ( int i = 0; i < values.length; i++ ) {
+			types[i].nullSafeSet( st, values[i], start + span, session );
+			span += types[i].getColumnSpan( getFactory() );
+		}
+		return span;
+	}
+
+	/**
+	 * Bind named parameters to the <tt>PreparedStatement</tt>. This has an empty
+	 * implementation on this superclass and should be implemented by subclasses
+	 * (queries) which allow named parameters.
+	 */
+	protected int bindNamedParameters(
+			final PreparedStatement ps,
+			final Map namedParams,
+			final int start,
+			final SessionImplementor session) throws SQLException, HibernateException {
+		if ( namedParams != null ) {
+			// assumes that types are all of span 1
+			Iterator iter = namedParams.entrySet().iterator();
+			int result = 0;
+			while ( iter.hasNext() ) {
+				Map.Entry e = ( Map.Entry ) iter.next();
+				String name = ( String ) e.getKey();
+				TypedValue typedval = ( TypedValue ) e.getValue();
+				int[] locs = getNamedParameterLocs( name );
+				for ( int i = 0; i < locs.length; i++ ) {
+					if ( log.isDebugEnabled() ) {
+						log.debug(
+								"bindNamedParameters() " +
+								typedval.getValue() + " -> " + name +
+								" [" + ( locs[i] + start ) + "]"
+							);
+					}
+					typedval.getType().nullSafeSet( ps, typedval.getValue(), locs[i] + start, session );
+				}
+				result += locs.length;
+			}
+			return result;
+		}
+		else {
+			return 0;
+		}
+	}
+
+	public int[] getNamedParameterLocs(String name) {
+		throw new AssertionFailure("no named parameters");
+	}
+
+	/**
 	 * Fetch a <tt>PreparedStatement</tt>, call <tt>setMaxRows</tt> and then execute it,
 	 * advance to the first result and return an SQL <tt>ResultSet</tt>
 	 */
@@ -1717,49 +1768,6 @@
 	}
 
 	/**
-	 * Bind named parameters to the <tt>PreparedStatement</tt>. This has an empty
-	 * implementation on this superclass and should be implemented by subclasses
-	 * (queries) which allow named parameters.
-	 */
-	protected int bindNamedParameters(final PreparedStatement ps,
-			  final Map namedParams,
-			  final int start,
-			  final SessionImplementor session)
-	throws SQLException, HibernateException {
-	
-		if ( namedParams != null ) {
-			// assumes that types are all of span 1
-			Iterator iter = namedParams.entrySet().iterator();
-			int result = 0;
-			while ( iter.hasNext() ) {
-				Map.Entry e = ( Map.Entry ) iter.next();
-				String name = ( String ) e.getKey();
-				TypedValue typedval = ( TypedValue ) e.getValue();
-				int[] locs = getNamedParameterLocs( name );
-				for ( int i = 0; i < locs.length; i++ ) {
-					if ( log.isDebugEnabled() ) {
-						log.debug( 
-								"bindNamedParameters() " +
-								typedval.getValue() + " -> " + name +
-								" [" + ( locs[i] + start ) + "]" 
-							);
-					}
-					typedval.getType().nullSafeSet( ps, typedval.getValue(), locs[i] + start, session );
-				}
-				result += locs.length;
-			}
-			return result;
-		}
-		else {
-			return 0;
-		}
-	}
-	
-	public int[] getNamedParameterLocs(String name) {
-		throw new AssertionFailure("no named parameters");
-	}
-
-	/**
 	 * Called by subclasses that load entities
 	 * @param persister only needed for logging
 	 */

Modified: trunk/Hibernate3/src/org/hibernate/loader/hql/QueryLoader.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/loader/hql/QueryLoader.java	2006-10-19 03:25:41 UTC (rev 10614)
+++ trunk/Hibernate3/src/org/hibernate/loader/hql/QueryLoader.java	2006-10-19 03:27:04 UTC (rev 10615)
@@ -9,6 +9,9 @@
 import java.util.List;
 import java.util.Map;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
 import org.hibernate.HibernateException;
 import org.hibernate.LockMode;
 import org.hibernate.QueryException;
@@ -22,11 +25,12 @@
 import org.hibernate.event.EventSource;
 import org.hibernate.exception.JDBCExceptionHelper;
 import org.hibernate.hql.HolderInstantiator;
-import org.hibernate.hql.QueryTranslator;
+import org.hibernate.hql.ast.QueryTranslatorImpl;
 import org.hibernate.hql.ast.tree.FromElement;
 import org.hibernate.hql.ast.tree.SelectClause;
 import org.hibernate.impl.IteratorImpl;
 import org.hibernate.loader.BasicLoader;
+import org.hibernate.param.ParameterSpecification;
 import org.hibernate.persister.collection.CollectionPersister;
 import org.hibernate.persister.collection.QueryableCollection;
 import org.hibernate.persister.entity.Loadable;
@@ -36,8 +40,6 @@
 import org.hibernate.type.EntityType;
 import org.hibernate.type.Type;
 import org.hibernate.util.ArrayHelper;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
 
 /**
  * A delegate that implements the Loader part of QueryTranslator.
@@ -51,7 +53,7 @@
 	/**
 	 * The query translator that is delegating to this object.
 	 */
-	private QueryTranslator queryTranslator;
+	private QueryTranslatorImpl queryTranslator;
 
 	private Queryable[] entityPersisters;
 	private String[] entityAliases;
@@ -82,6 +84,7 @@
 
 	private LockMode[] defaultLockModes;
 
+	private final boolean isCollectionFilter;
 
 
 
@@ -89,14 +92,16 @@
 	 * Creates a new Loader implementation.
 	 *
 	 * @param queryTranslator The query translator that is the delegator.
-	 * @param factory         The factory from which this loader is being created.
+	 * @param factory The factory from which this loader is being created.
+	 * @param selectClause The AST representing the select clause for loading.
 	 */
 	public QueryLoader(
-			final QueryTranslator queryTranslator,
+			final QueryTranslatorImpl queryTranslator,
 	        final SessionFactoryImplementor factory,
 	        final SelectClause selectClause) {
 		super( factory );
 		this.queryTranslator = queryTranslator;
+		this.isCollectionFilter = selectClause.getWalker().isFilter();
 		initialize( selectClause );
 		postInstantiate();
 	}
@@ -483,53 +488,44 @@
 		return queryTranslator.getParameterTranslations().getNamedParameterSqlLocations( name );
 	}
 
-	protected int bindNamedParameters(
-			final PreparedStatement ps,
-	        final Map namedParams,
-	        final int start,
-	        final SessionImplementor session) throws SQLException, HibernateException {
-		// we override binding of named parameters here because the parser
-		// keeps track of named-parameter SQL positions "absolutely"; or at
-		// least it thinks it does.  But currently it does not know about filter
-		// parameters.  Thus we need to account for the number of filter-injected
-		// parameters and adjust the bind positions for named parameters
-		// accordingly...
-		final int filterParamCountAdjustment = start - queryTranslator.getParameterTranslations().getOrdinalParameterCount();
-		if ( namedParams != null ) {
-			// assumes that types are all of span 1
-			Iterator iter = namedParams.entrySet().iterator();
-			int result = 0;
-			while ( iter.hasNext() ) {
-				Map.Entry e = ( Map.Entry ) iter.next();
-				String name = ( String ) e.getKey();
-				TypedValue typedval = ( TypedValue ) e.getValue();
-				int[] locs = getNamedParameterLocs( name );
-				for ( int i = 0; i < locs.length; i++ ) {
-					final int location = locs[i] + filterParamCountAdjustment;
-					if ( log.isDebugEnabled() ) {
-						log.debug(
-								"bindNamedParameters() " +
-								typedval.getValue() + " -> " + name +
-								" [" + location + "]"
-							);
-					}
-					try {
-						typedval.getType().nullSafeSet( ps, typedval.getValue(), location, session );
-					}
-					catch( ClassCastException cce ) {
-						throw new TypeMismatchException(
-								"named parameter [" + name + "] not of expected type; expected = " +
-								typedval.getType().getReturnedClass() + "; but was =" +
-								 typedval.getValue().getClass().getName()
-						);
-					}
-				}
-				result += locs.length;
-			}
-			return result;
+	protected int bindParameterValues(
+			PreparedStatement st,
+			QueryParameters queryParameters,
+			int startPosition,
+			SessionImplementor session) throws SQLException {
+		int position = startPosition;
+		position = bindFilterParameterValues( st, queryParameters, position, session );
+		List parameterSpecs = queryTranslator.getSqlAST().getWalker().getParameters();
+		Iterator itr = parameterSpecs.iterator();
+		while ( itr.hasNext() ) {
+			ParameterSpecification spec = ( ParameterSpecification ) itr.next();
+			position += spec.bind( st, queryParameters, session, position );
 		}
-		else {
-			return 0;
+		return position;
+	}
+
+	private int bindFilterParameterValues(
+			PreparedStatement st,
+			QueryParameters queryParameters,
+			int position,
+			SessionImplementor session) throws SQLException {
+		// todo : better to handle dynamic filters through implicit DynamicFilterParameterSpecification
+		// see the discussion there in DynamicFilterParameterSpecification's javadocs as to why
+		// it is currently not done that way.
+		int filteredParamCount = queryParameters.getFilteredPositionalParameterTypes() == null
+				? 0
+				: queryParameters.getFilteredPositionalParameterTypes().length;
+		int nonfilteredParamCount = queryParameters.getPositionalParameterTypes() == null
+				? 0
+				: queryParameters.getPositionalParameterTypes().length;
+		int filterParamCount = filteredParamCount - nonfilteredParamCount;
+		for ( int i = 0; i < filterParamCount; i++ ) {
+			Type type = queryParameters.getFilteredPositionalParameterTypes()[i];
+			Object value = queryParameters.getFilteredPositionalParameterValues()[i];
+			type.nullSafeSet( st, value, position, session );
+			position += type.getColumnSpan( getFactory() );
 		}
+
+		return position;
 	}
 }

Added: trunk/Hibernate3/src/org/hibernate/param/CollectionFilterKeyParameterSpecification.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/param/CollectionFilterKeyParameterSpecification.java	2006-10-19 03:25:41 UTC (rev 10614)
+++ trunk/Hibernate3/src/org/hibernate/param/CollectionFilterKeyParameterSpecification.java	2006-10-19 03:27:04 UTC (rev 10615)
@@ -0,0 +1,57 @@
+package org.hibernate.param;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * A specialized ParameterSpecification impl for dealing with a collection-key
+ * as part of a collection filter compilation.
+ *
+ * @author Steve Ebersole
+ */
+public class CollectionFilterKeyParameterSpecification implements ParameterSpecification {
+
+	private final String collectionRole;
+	private final Type keyType;
+	private final int queryParameterPosition;
+
+	/**
+	 * Creates a specialized collection-filter collection-key parameter spec.
+	 *
+	 * @param collectionRole The collection role being filtered.
+	 * @param keyType The mapped collection-key type.
+	 * @param queryParameterPosition The position within {@link org.hibernate.engine.QueryParameters} where
+	 * we can find the appropriate param value to bind.
+	 */
+	public CollectionFilterKeyParameterSpecification(String collectionRole, Type keyType, int queryParameterPosition) {
+		this.collectionRole = collectionRole;
+		this.keyType = keyType;
+		this.queryParameterPosition = queryParameterPosition;
+	}
+
+	public int bind(
+			PreparedStatement statement,
+			QueryParameters qp,
+			SessionImplementor session,
+			int position) throws SQLException {
+		Object value = qp.getPositionalParameterValues()[queryParameterPosition];
+		keyType.nullSafeSet( statement, value, position, session );
+		return keyType.getColumnSpan( session.getFactory() );
+	}
+
+	public Type getExpectedType() {
+		return keyType;
+	}
+
+	public void setExpectedType(Type expectedType) {
+		// todo : throw exception?
+	}
+
+	public String renderDisplayInfo() {
+		return "collection-filter-key=" + collectionRole;
+	}
+}

Added: trunk/Hibernate3/src/org/hibernate/param/DynamicFilterParameterSpecification.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/param/DynamicFilterParameterSpecification.java	2006-10-19 03:25:41 UTC (rev 10614)
+++ trunk/Hibernate3/src/org/hibernate/param/DynamicFilterParameterSpecification.java	2006-10-19 03:27:04 UTC (rev 10615)
@@ -0,0 +1,60 @@
+package org.hibernate.param;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import org.hibernate.engine.QueryParameters;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * A specialized ParameterSpecification impl for dealing with a dynamic filter
+ * parameters.
+ * <p/>
+ * Note: this class is currently not used.  The ideal way to deal with dynamic filter
+ * parameters for HQL would be to track them just as we do with other parameters
+ * in the translator.  However, the problem with that is that we currently do not
+ * know the filters which actually apply to the query; we know the active/enabled ones,
+ * but not the ones that actually "make it into" the resulting query.
+ *
+ * @author Steve Ebersole
+ */
+public class DynamicFilterParameterSpecification implements ParameterSpecification {
+	private final String filterName;
+	private final String parameterName;
+	private final Type definedParameterType;
+	private final int queryParameterPosition;
+
+	public DynamicFilterParameterSpecification(
+			String filterName,
+			String parameterName,
+			Type definedParameterType,
+			int queryParameterPosition) {
+		this.filterName = filterName;
+		this.parameterName = parameterName;
+		this.definedParameterType = definedParameterType;
+		this.queryParameterPosition = queryParameterPosition;
+	}
+
+	public int bind(
+			PreparedStatement statement,
+			QueryParameters qp,
+			SessionImplementor session,
+			int position) throws SQLException {
+		Object value = qp.getFilteredPositionalParameterValues()[queryParameterPosition];
+		definedParameterType.nullSafeSet( statement, value, position, session );
+		return definedParameterType.getColumnSpan( session.getFactory() );
+	}
+
+	public Type getExpectedType() {
+		return definedParameterType;
+	}
+
+	public void setExpectedType(Type expectedType) {
+		// todo : throw exception?
+	}
+
+	public String renderDisplayInfo() {
+		return "dynamic-filter={filterName=" + filterName + ",paramName=" + parameterName + "}";
+	}
+}

Modified: trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java	2006-10-19 03:25:41 UTC (rev 10614)
+++ trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java	2006-10-19 03:27:04 UTC (rev 10615)
@@ -198,6 +198,29 @@
 		s.close();
 	}
 
+	public void testComponentParameterBinding() {
+		// HHH-1774 : parameters are bound incorrectly with component parameters...
+		Session s = openSession();
+		s.beginTransaction();
+
+		Order.Id oId = new Order.Id( "1234", 1 );
+
+		// control
+		s.createQuery("from Order o where o.customer.name =:name and o.id = :id")
+				.setParameter( "name", "oracle" )
+				.setParameter( "id", oId )
+				.list();
+
+		// this is the form that caused problems in the original case...
+		s.createQuery("from Order o where o.id = :id and o.customer.name =:name ")
+				.setParameter( "id", oId )
+				.setParameter( "name", "oracle" )
+				.list();
+
+		s.getTransaction().commit();
+		s.close();
+	}
+
 	public void testAnyMappingReference() {
 		Session s = openSession();
 		s.beginTransaction();




More information about the hibernate-commits mailing list