[hibernate-commits] Hibernate SVN: r10614 - in branches/Branch_3_2/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:25:45 EDT 2006
Author: steve.ebersole at jboss.com
Date: 2006-10-18 23:25:41 -0400 (Wed, 18 Oct 2006)
New Revision: 10614
Added:
branches/Branch_3_2/Hibernate3/src/org/hibernate/param/CollectionFilterKeyParameterSpecification.java
branches/Branch_3_2/Hibernate3/src/org/hibernate/param/DynamicFilterParameterSpecification.java
Modified:
branches/Branch_3_2/Hibernate3/src/org/hibernate/action/EntityUpdateAction.java
branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/HqlSqlWalker.java
branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/ParameterTranslationsImpl.java
branches/Branch_3_2/Hibernate3/src/org/hibernate/loader/Loader.java
branches/Branch_3_2/Hibernate3/src/org/hibernate/loader/hql/QueryLoader.java
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java
Log:
HHH-1774 : proper binding of multi-column parameter types to queries
Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/action/EntityUpdateAction.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/action/EntityUpdateAction.java 2006-10-19 01:44:38 UTC (rev 10613)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/action/EntityUpdateAction.java 2006-10-19 03:25:41 UTC (rev 10614)
@@ -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 ( entry.getStatus()==Status.MANAGED || persister.isVersionPropertyGenerated() ) {
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,7 +140,7 @@
nextVersion,
getSession(),
instance
- );
+ );
cacheEntry = persister.getCacheEntryStructure().structure(ce);
// boolean put = persister.getCache().update(ck, cacheEntry);
boolean put = persister.getCache().update( ck, cacheEntry, nextVersion, previousVersion );
@@ -156,7 +150,6 @@
.secondLevelCachePut( getPersister().getCache().getRegionName() );
}
}
-
}
postUpdate();
Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/HqlSqlWalker.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/HqlSqlWalker.java 2006-10-19 01:44:38 UTC (rev 10613)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/HqlSqlWalker.java 2006-10-19 03:25:41 UTC (rev 10614)
@@ -15,6 +15,7 @@
import org.apache.commons.logging.LogFactory;
import org.hibernate.QueryException;
import org.hibernate.HibernateException;
+import org.hibernate.impl.FilterImpl;
import org.hibernate.engine.JoinSequence;
import org.hibernate.engine.ParameterBinder;
import org.hibernate.engine.SessionFactoryImplementor;
@@ -60,6 +61,8 @@
import org.hibernate.param.ParameterSpecification;
import org.hibernate.param.PositionalParameterSpecification;
import org.hibernate.param.VersionTypeSeedParameterSpecification;
+import org.hibernate.param.CollectionFilterKeyParameterSpecification;
+import org.hibernate.param.DynamicFilterParameterSpecification;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.JoinFragment;
@@ -87,136 +90,125 @@
* @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();
this.queryTranslatorImpl = qti;
this.sessionFactoryHelper = new SessionFactoryHelper( sfi );
this.literalProcessor = new LiteralProcessor( this );
this.tokenReplacements = tokenReplacements;
- this.filterCollectionRole = collectionRole;
this.hqlParser = parser;
this.printer = new ASTPrinter( SqlTokenTypes.class );
+ this.collectionFilterRole = collectionRole;
}
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 +262,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: branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/ParameterTranslationsImpl.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/ParameterTranslationsImpl.java 2006-10-19 01:44:38 UTC (rev 10613)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/hql/ast/ParameterTranslationsImpl.java 2006-10-19 03:25:41 UTC (rev 10614)
@@ -19,7 +19,7 @@
* Defines the information available for parameters encountered during
* query translation through the antlr-based parser.
*
- * @author <a href="mailto:steve at hibernate.org">Steve Ebersole </a>
+ * @author Steve Ebersole
*/
public class ParameterTranslationsImpl implements ParameterTranslations {
@@ -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: branches/Branch_3_2/Hibernate3/src/org/hibernate/loader/Loader.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/loader/Loader.java 2006-10-19 01:44:38 UTC (rev 10613)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/loader/Loader.java 2006-10-19 03:25:41 UTC (rev 10614)
@@ -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.
@@ -1525,9 +1505,7 @@
final QueryParameters queryParameters,
final boolean scroll,
final SessionImplementor session) throws SQLException, HibernateException {
-
- queryParameters.processFilters( getSQLString(), session );
- String sql = queryParameters.getFilteredSQL();
+ String sql = processFilters( queryParameters, session );
final Dialect dialect = getFactory().getDialect();
final RowSelection selection = queryParameters.getRowSelection();
boolean useLimit = useLimit( selection, dialect );
@@ -1572,9 +1550,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 );
}
@@ -1601,6 +1579,11 @@
return st;
}
+ protected String processFilters(QueryParameters queryParameters, SessionImplementor session) {
+ queryParameters.processFilters( getSQLString(), session );
+ return queryParameters.getFilteredSQL();
+ }
+
/**
* Some dialect-specific LIMIT clauses require the maximium last row number,
* others require the maximum returned row count.
@@ -1646,7 +1629,80 @@
}
}
+ 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 +1773,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: branches/Branch_3_2/Hibernate3/src/org/hibernate/loader/hql/QueryLoader.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/loader/hql/QueryLoader.java 2006-10-19 01:44:38 UTC (rev 10613)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/loader/hql/QueryLoader.java 2006-10-19 03:25:41 UTC (rev 10614)
@@ -9,24 +9,26 @@
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;
import org.hibernate.ScrollableResults;
-import org.hibernate.TypeMismatchException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.QueryParameters;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.engine.SessionImplementor;
-import org.hibernate.engine.TypedValue;
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 +38,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.
@@ -48,10 +48,8 @@
private static final Log log = LogFactory.getLog( QueryLoader.class );
- /**
- * The query translator that is delegating to this object.
- */
- private QueryTranslator queryTranslator;
+ private final QueryTranslatorImpl queryTranslator;
+ private final boolean isCollectionFilter;
private Queryable[] entityPersisters;
private String[] entityAliases;
@@ -63,7 +61,6 @@
private boolean hasScalars;
private String[][] scalarColumnNames;
- //private Type[] sqlResultTypes;
private Type[] queryReturnTypes;
private final Map sqlAliasByEntityAlias = new HashMap(8);
@@ -83,28 +80,26 @@
private LockMode[] defaultLockModes;
-
-
/**
* 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();
}
private void initialize(SelectClause selectClause) {
-
List fromElementList = selectClause.getFromElementsForLoad();
-
hasScalars = selectClause.isScalarSelect();
scalarColumnNames = selectClause.getColumnNames();
//sqlResultTypes = selectClause.getSqlResultTypes();
@@ -483,53 +478,45 @@
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: branches/Branch_3_2/Hibernate3/src/org/hibernate/param/CollectionFilterKeyParameterSpecification.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/param/CollectionFilterKeyParameterSpecification.java 2006-10-19 01:44:38 UTC (rev 10613)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/param/CollectionFilterKeyParameterSpecification.java 2006-10-19 03:25:41 UTC (rev 10614)
@@ -0,0 +1,58 @@
+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: branches/Branch_3_2/Hibernate3/src/org/hibernate/param/DynamicFilterParameterSpecification.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/param/DynamicFilterParameterSpecification.java 2006-10-19 01:44:38 UTC (rev 10613)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/param/DynamicFilterParameterSpecification.java 2006-10-19 03:25:41 UTC (rev 10614)
@@ -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: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java 2006-10-19 01:44:38 UTC (rev 10613)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java 2006-10-19 03:25:41 UTC (rev 10614)
@@ -125,6 +125,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