diff --git a/src/org/hibernate/criterion/SubqueryExpression.java b/src/org/hibernate/criterion/SubqueryExpression.java index 9b5d5c42eb..cbc35a391c 100755 --- a/src/org/hibernate/criterion/SubqueryExpression.java +++ b/src/org/hibernate/criterion/SubqueryExpression.java @@ -1,14 +1,13 @@ //$Id$ package org.hibernate.criterion; -import java.util.HashMap; - import org.hibernate.Criteria; import org.hibernate.EntityMode; import org.hibernate.HibernateException; import org.hibernate.engine.QueryParameters; import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.engine.TypedValue; +import org.hibernate.engine.SessionImplementor; import org.hibernate.impl.CriteriaImpl; import org.hibernate.loader.criteria.CriteriaJoinWalker; import org.hibernate.loader.criteria.CriteriaQueryTranslator; @@ -19,7 +18,7 @@ import org.hibernate.type.Type; * @author Gavin King */ public abstract class SubqueryExpression implements Criterion { - + private CriteriaImpl criteriaImpl; private String quantifier; private String op; @@ -30,49 +29,67 @@ public abstract class SubqueryExpression implements Criterion { protected Type[] getTypes() { return types; } - + protected SubqueryExpression(String op, String quantifier, DetachedCriteria dc) { this.criteriaImpl = dc.getCriteriaImpl(); this.quantifier = quantifier; this.op = op; } - + protected abstract String toLeftSqlString(Criteria criteria, CriteriaQuery outerQuery); - public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) - throws HibernateException { - + public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException { final SessionFactoryImplementor factory = criteriaQuery.getFactory(); - final OuterJoinLoadable persister = (OuterJoinLoadable) factory.getEntityPersister( criteriaImpl.getEntityOrClassName() ); + final OuterJoinLoadable persister = + ( OuterJoinLoadable ) factory.getEntityPersister( criteriaImpl.getEntityOrClassName() ); createAndSetInnerQuery( criteriaQuery, factory ); - + criteriaImpl.setSession( deriveRootSession( criteria ) ); + CriteriaJoinWalker walker = new CriteriaJoinWalker( persister, innerQuery, factory, criteriaImpl, criteriaImpl.getEntityOrClassName(), - new HashMap(), - innerQuery.getRootSQLALias()); + criteriaImpl.getSession().getEnabledFilters(), + innerQuery.getRootSQLALias() + ); String sql = walker.getSQLString(); - final StringBuffer buf = new StringBuffer() - .append( toLeftSqlString(criteria, criteriaQuery) ); - if (op!=null) buf.append(' ').append(op).append(' '); - if (quantifier!=null) buf.append(quantifier).append(' '); - return buf.append('(').append(sql).append(')') - .toString(); + final StringBuffer buf = new StringBuffer( toLeftSqlString(criteria, criteriaQuery) ); + if ( op != null ) { + buf.append( ' ' ).append( op ).append( ' ' ); + } + if ( quantifier != null ) { + buf.append( quantifier ).append( ' ' ); + } + return buf.append( '(' ).append( sql ).append( ')' ) + .toString(); } - public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery) + private SessionImplementor deriveRootSession(Criteria criteria) { + if ( criteria instanceof CriteriaImpl ) { + return ( ( CriteriaImpl ) criteria ).getSession(); + } + else if ( criteria instanceof CriteriaImpl.Subcriteria ) { + return deriveRootSession( ( ( CriteriaImpl.Subcriteria ) criteria ).getParent() ); + } + else { + // could happen for custom Criteria impls. Not likely, but... + // for long term solution, see HHH-3514 + return null; + } + } + + public TypedValue[] getTypedValues(Criteria criteria, CriteriaQuery criteriaQuery) throws HibernateException { //the following two lines were added to ensure that this.params is not null, which //can happen with two-deep nested subqueries SessionFactoryImplementor factory = criteriaQuery.getFactory(); createAndSetInnerQuery(criteriaQuery, factory); - + Type[] ppTypes = params.getPositionalParameterTypes(); Object[] ppValues = params.getPositionalParameterValues(); TypedValue[] tv = new TypedValue[ppTypes.length]; @@ -83,12 +100,12 @@ public abstract class SubqueryExpression implements Criterion { } /** - * Creates the inner query used to extract some useful information about - * types, since it is needed in both methods. - * @param criteriaQuery - * @param factory + * Creates the inner query used to extract some useful information about types, since it is needed in both methods. + * + * @param criteriaQuery The criteria query + * @param factory The session factory. */ - private void createAndSetInnerQuery(CriteriaQuery criteriaQuery, final SessionFactoryImplementor factory) { + private void createAndSetInnerQuery(CriteriaQuery criteriaQuery, SessionFactoryImplementor factory) { if ( innerQuery == null ) { //with two-deep subqueries, the same alias would get generated for //both using the old method (criteriaQuery.generateSQLAlias()), so diff --git a/src/org/hibernate/engine/QueryParameters.java b/src/org/hibernate/engine/QueryParameters.java index e4f35411b6..427f592c21 100644 --- a/src/org/hibernate/engine/QueryParameters.java +++ b/src/org/hibernate/engine/QueryParameters.java @@ -3,7 +3,6 @@ package org.hibernate.engine; import java.io.Serializable; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; @@ -16,6 +15,7 @@ import org.apache.commons.logging.LogFactory; import org.hibernate.HibernateException; import org.hibernate.QueryException; import org.hibernate.ScrollMode; +import org.hibernate.impl.FilterImpl; import org.hibernate.dialect.Dialect; import org.hibernate.hql.classic.ParserHelper; import org.hibernate.pretty.Printer; @@ -46,29 +46,28 @@ public final class QueryParameters { private boolean callable = false; private boolean autodiscovertypes = false; private boolean isNaturalKeyLookup; - + private final ResultTransformer resultTransformer; // why is all others non final ? - + private String processedSQL; private Type[] processedPositionalParameterTypes; private Object[] processedPositionalParameterValues; - + public QueryParameters() { this( ArrayHelper.EMPTY_TYPE_ARRAY, ArrayHelper.EMPTY_OBJECT_ARRAY ); } public QueryParameters(Type type, Object value) { - this( new Type[] {type}, new Object[] {value} ); + this( new Type[] { type }, new Object[] { value } ); } public QueryParameters( - final Type[] positionalParameterTypes, - final Object[] postionalParameterValues, - final Object optionalObject, - final String optionalEntityName, - final Serializable optionalObjectId - ) { - this(positionalParameterTypes, postionalParameterValues); + final Type[] positionalParameterTypes, + final Object[] postionalParameterValues, + final Object optionalObject, + final String optionalEntityName, + final Serializable optionalObjectId) { + this( positionalParameterTypes, postionalParameterValues ); this.optionalObject = optionalObject; this.optionalId = optionalObjectId; this.optionalEntityName = optionalEntityName; @@ -76,42 +75,24 @@ public final class QueryParameters { } public QueryParameters( - final Type[] positionalParameterTypes, - final Object[] postionalParameterValues - ) { - this( - positionalParameterTypes, - postionalParameterValues, - null, - null, - false, - null, - null, - false, - null - ); + final Type[] positionalParameterTypes, + final Object[] postionalParameterValues) { + this( positionalParameterTypes, postionalParameterValues, null, null, false, null, null, false, null ); } public QueryParameters( - final Type[] positionalParameterTypes, - final Object[] postionalParameterValues, - final Serializable[] collectionKeys - ) { - this( - positionalParameterTypes, - postionalParameterValues, - null, - collectionKeys - ); + final Type[] positionalParameterTypes, + final Object[] postionalParameterValues, + final Serializable[] collectionKeys) { + this( positionalParameterTypes, postionalParameterValues, null, collectionKeys ); } public QueryParameters( final Type[] positionalParameterTypes, final Object[] postionalParameterValues, final Map namedParameters, - final Serializable[] collectionKeys - ) { - this( + final Serializable[] collectionKeys) { + this( positionalParameterTypes, postionalParameterValues, namedParameters, @@ -119,37 +100,36 @@ public final class QueryParameters { null, false, false, - null, + null, null, collectionKeys, null - ); - } + ); + } public QueryParameters( - final Type[] positionalParameterTypes, - final Object[] positionalParameterValues, - final Map lockModes, - final RowSelection rowSelection, - final boolean cacheable, - final String cacheRegion, - //final boolean forceCacheRefresh, - final String comment, - final boolean isLookupByNaturalKey, - final ResultTransformer transformer - ) { + final Type[] positionalParameterTypes, + final Object[] positionalParameterValues, + final Map lockModes, + final RowSelection rowSelection, + final boolean cacheable, + final String cacheRegion, + //final boolean forceCacheRefresh, + final String comment, + final boolean isLookupByNaturalKey, + final ResultTransformer transformer) { this( - positionalParameterTypes, - positionalParameterValues, - null, - lockModes, - rowSelection, - false, - cacheable, - cacheRegion, - comment, - null, - transformer + positionalParameterTypes, + positionalParameterValues, + null, + lockModes, + rowSelection, + false, + cacheable, + cacheRegion, + comment, + null, + transformer ); isNaturalKeyLookup = isLookupByNaturalKey; } @@ -166,8 +146,7 @@ public final class QueryParameters { //final boolean forceCacheRefresh, final String comment, final Serializable[] collectionKeys, - ResultTransformer transformer - ) { + ResultTransformer transformer) { this.positionalParameterTypes = positionalParameterTypes; this.positionalParameterValues = positionalParameterValues; this.namedParameters = namedParameters; @@ -181,36 +160,35 @@ public final class QueryParameters { this.readOnly = readOnly; this.resultTransformer = transformer; } - + public QueryParameters( - final Type[] positionalParameterTypes, - final Object[] positionalParameterValues, - final Map namedParameters, - final Map lockModes, - final RowSelection rowSelection, - final boolean readOnly, - final boolean cacheable, - final String cacheRegion, - //final boolean forceCacheRefresh, - final String comment, - final Serializable[] collectionKeys, - final Object optionalObject, - final String optionalEntityName, - final Serializable optionalId, - final ResultTransformer transformer - ) { + final Type[] positionalParameterTypes, + final Object[] positionalParameterValues, + final Map namedParameters, + final Map lockModes, + final RowSelection rowSelection, + final boolean readOnly, + final boolean cacheable, + final String cacheRegion, + //final boolean forceCacheRefresh, + final String comment, + final Serializable[] collectionKeys, + final Object optionalObject, + final String optionalEntityName, + final Serializable optionalId, + final ResultTransformer transformer) { this( - positionalParameterTypes, - positionalParameterValues, - namedParameters, - lockModes, - rowSelection, - readOnly, - cacheable, - cacheRegion, - comment, - collectionKeys, - transformer + positionalParameterTypes, + positionalParameterValues, + namedParameters, + lockModes, + rowSelection, + readOnly, + cacheable, + cacheRegion, + comment, + collectionKeys, + transformer ); this.optionalEntityName = optionalEntityName; this.optionalId = optionalId; @@ -218,7 +196,7 @@ public final class QueryParameters { } public boolean hasRowSelection() { - return rowSelection!=null; + return rowSelection != null; } public Map getNamedParameters() { @@ -236,7 +214,7 @@ public final class QueryParameters { public RowSelection getRowSelection() { return rowSelection; } - + public ResultTransformer getResultTransformer() { return resultTransformer; } @@ -266,15 +244,15 @@ public final class QueryParameters { } public void traceParameters(SessionFactoryImplementor factory) throws HibernateException { - Printer print = new Printer(factory); - if (positionalParameterValues.length!=0) { + Printer print = new Printer( factory ); + if ( positionalParameterValues.length != 0 ) { log.trace( - "parameters: " + - print.toString(positionalParameterTypes, positionalParameterValues) - ); + "parameters: " + + print.toString( positionalParameterTypes, positionalParameterValues ) + ); } - if (namedParameters!=null) { - log.trace( "named parameters: " + print.toString(namedParameters) ); + if ( namedParameters != null ) { + log.trace( "named parameters: " + print.toString( namedParameters ) ); } } @@ -295,13 +273,13 @@ public final class QueryParameters { } public void validateParameters() throws QueryException { - int types = positionalParameterTypes==null ? 0 : positionalParameterTypes.length; - int values = positionalParameterValues==null ? 0 : positionalParameterValues.length; - if (types!=values) { + int types = positionalParameterTypes == null ? 0 : positionalParameterTypes.length; + int values = positionalParameterValues == null ? 0 : positionalParameterValues.length; + if ( types != values ) { throw new QueryException( - "Number of positional parameter types:" + types + - " does not match number of positional parameters: " + values - ); + "Number of positional parameter types:" + types + + " does not match number of positional parameters: " + values + ); } } @@ -362,44 +340,49 @@ public final class QueryParameters { } public void setCallable(boolean callable) { - this.callable = callable; + this.callable = callable; } public boolean isCallable() { return callable; } - + public boolean hasAutoDiscoverScalarTypes() { return autodiscovertypes; } public void processFilters(String sql, SessionImplementor session) { - - if ( session.getEnabledFilters().size()==0 || sql.indexOf(ParserHelper.HQL_VARIABLE_PREFIX)<0 ) { + processFilters( sql, session.getEnabledFilters(), session.getFactory() ); + } + + public void processFilters(String sql, Map filters, SessionFactoryImplementor factory) { + if ( filters.size() == 0 || sql.indexOf( ParserHelper.HQL_VARIABLE_PREFIX ) < 0 ) { // HELLA IMPORTANT OPTIMIZATION!!! processedPositionalParameterValues = getPositionalParameterValues(); processedPositionalParameterTypes = getPositionalParameterTypes(); processedSQL = sql; } else { - - Dialect dialect = session.getFactory().getDialect(); + final Dialect dialect = factory.getDialect(); String symbols = new StringBuffer().append( ParserHelper.HQL_SEPARATORS ) .append( dialect.openQuote() ) .append( dialect.closeQuote() ) .toString(); StringTokenizer tokens = new StringTokenizer( sql, symbols, true ); StringBuffer result = new StringBuffer(); - + List parameters = new ArrayList(); List parameterTypes = new ArrayList(); - + + int positionalIndex = 0; while ( tokens.hasMoreTokens() ) { final String token = tokens.nextToken(); if ( token.startsWith( ParserHelper.HQL_VARIABLE_PREFIX ) ) { - String filterParameterName = token.substring( 1 ); - Object value = session.getFilterParameterValue( filterParameterName ); - Type type = session.getFilterParameterType( filterParameterName ); + final String filterParameterName = token.substring( 1 ); + final String[] parts = parseFilterParameterName( filterParameterName ); + final FilterImpl filter = ( FilterImpl ) filters.get( parts[0] ); + final Object value = filter.getParameter( parts[1] ); + final Type type = filter.getFilterDefinition().getParameterType( parts[1] ); if ( value != null && Collection.class.isAssignableFrom( value.getClass() ) ) { Iterator itr = ( ( Collection ) value ).iterator(); while ( itr.hasNext() ) { @@ -419,15 +402,17 @@ public final class QueryParameters { } } else { + if ( "?".equals( token ) && positionalIndex < getPositionalParameterValues().length ) { + parameters.add( getPositionalParameterValues()[positionalIndex] ); + parameterTypes.add( getPositionalParameterTypes()[positionalIndex] ); + positionalIndex++; + } result.append( token ); } } - parameters.addAll( Arrays.asList( getPositionalParameterValues() ) ); - parameterTypes.addAll( Arrays.asList( getPositionalParameterTypes() ) ); processedPositionalParameterValues = parameters.toArray(); - processedPositionalParameterTypes = ( Type[] ) parameterTypes.toArray( new Type[0] ); + processedPositionalParameterTypes = ( Type[] ) parameterTypes.toArray( new Type[parameterTypes.size()] ); processedSQL = result.toString(); - } } @@ -458,16 +443,16 @@ public final class QueryParameters { public QueryParameters createCopyUsing(RowSelection selection) { QueryParameters copy = new QueryParameters( this.positionalParameterTypes, - this.positionalParameterValues, - this.namedParameters, - this.lockModes, - selection, - this.readOnly, - this.cacheable, - this.cacheRegion, - this.comment, - this.collectionKeys, - this.optionalObject, + this.positionalParameterValues, + this.namedParameters, + this.lockModes, + selection, + this.readOnly, + this.cacheable, + this.cacheRegion, + this.comment, + this.collectionKeys, + this.optionalObject, this.optionalEntityName, this.optionalId, this.resultTransformer @@ -478,5 +463,13 @@ public final class QueryParameters { return copy; } - + 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 }; + } } diff --git a/src/org/hibernate/hql/ast/HqlSqlWalker.java b/src/org/hibernate/hql/ast/HqlSqlWalker.java index 8684adb898..a4fa42bd31 100644 --- a/src/org/hibernate/hql/ast/HqlSqlWalker.java +++ b/src/org/hibernate/hql/ast/HqlSqlWalker.java @@ -209,6 +209,10 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par return collectionFilterRole != null; } + public String getCollectionFilterRole() { + return collectionFilterRole; + } + public SessionFactoryHelper getSessionFactoryHelper() { return sessionFactoryHelper; } @@ -531,8 +535,8 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par // After that, process the JOINs. // Invoke a delegate to do the work, as this is farily complex. - JoinProcessor joinProcessor = new JoinProcessor( astFactory, queryTranslatorImpl ); - joinProcessor.processJoins( qn, isSubQuery() ); + JoinProcessor joinProcessor = new JoinProcessor( this ); + joinProcessor.processJoins( qn ); // Attach any mapping-defined "ORDER BY" fragments Iterator itr = qn.getFromClause().getProjectionList().iterator(); @@ -570,13 +574,21 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par // Make #@%$^#^&# sure no alias is applied to the table name fromElement.setText( persister.getTableName() ); - // append any filter fragments; the EMPTY_MAP is used under the assumption that - // currently enabled filters should not affect this process - if ( persister.getDiscriminatorType() != null ) { - new SyntheticAndFactory( getASTFactory() ).addDiscriminatorWhereFragment( +// // append any filter fragments; the EMPTY_MAP is used under the assumption that +// // currently enabled filters should not affect this process +// if ( persister.getDiscriminatorType() != null ) { +// new SyntheticAndFactory( getASTFactory() ).addDiscriminatorWhereFragment( +// statement, +// persister, +// java.util.Collections.EMPTY_MAP, +// fromElement.getTableAlias() +// ); +// } + if ( persister.getDiscriminatorType() != null || ! queryTranslatorImpl.getEnabledFilters().isEmpty() ) { + new SyntheticAndFactory( this ).addDiscriminatorWhereFragment( statement, persister, - java.util.Collections.EMPTY_MAP, + queryTranslatorImpl.getEnabledFilters(), fromElement.getTableAlias() ); } diff --git a/src/org/hibernate/hql/ast/QueryTranslatorImpl.java b/src/org/hibernate/hql/ast/QueryTranslatorImpl.java index 5c027a95d4..522bf54b6e 100644 --- a/src/org/hibernate/hql/ast/QueryTranslatorImpl.java +++ b/src/org/hibernate/hql/ast/QueryTranslatorImpl.java @@ -74,6 +74,8 @@ public class QueryTranslatorImpl implements FilterTranslator { private String sql; private ParameterTranslations paramTranslations; + private List collectedParameterSpecifications; + /** * Creates a new AST-based query translator. @@ -217,6 +219,7 @@ public class QueryTranslatorImpl implements FilterTranslator { log.debug( "SQL: " + sql ); } gen.getParseErrorHandler().throwQueryException(); + collectedParameterSpecifications = gen.getCollectedParameters(); } } @@ -540,10 +543,15 @@ public class QueryTranslatorImpl implements FilterTranslator { public ParameterTranslations getParameterTranslations() { if ( paramTranslations == null ) { paramTranslations = new ParameterTranslationsImpl( getWalker().getParameters() ); +// paramTranslations = new ParameterTranslationsImpl( collectedParameterSpecifications ); } return paramTranslations; } + public List getCollectedParameterSpecifications() { + return collectedParameterSpecifications; + } + public static class JavaConstantConverter implements NodeTraverser.VisitationStrategy { private AST dotRoot; public void visit(AST node) { diff --git a/src/org/hibernate/hql/ast/SqlGenerator.java b/src/org/hibernate/hql/ast/SqlGenerator.java index 7adc781075..4246f9e050 100644 --- a/src/org/hibernate/hql/ast/SqlGenerator.java +++ b/src/org/hibernate/hql/ast/SqlGenerator.java @@ -4,16 +4,20 @@ package org.hibernate.hql.ast; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; +import java.util.Arrays; import antlr.RecognitionException; import antlr.collections.AST; import org.hibernate.QueryException; +import org.hibernate.param.ParameterSpecification; import org.hibernate.dialect.function.SQLFunction; import org.hibernate.engine.SessionFactoryImplementor; import org.hibernate.hql.antlr.SqlGeneratorBase; import org.hibernate.hql.ast.tree.MethodNode; import org.hibernate.hql.ast.tree.FromElement; import org.hibernate.hql.ast.tree.Node; +import org.hibernate.hql.ast.tree.ParameterNode; +import org.hibernate.hql.ast.tree.ParameterContainer; /** * Generates SQL by overriding callback methods in the base class, which does @@ -41,6 +45,12 @@ public class SqlGenerator extends SqlGeneratorBase implements ErrorReporter { private LinkedList outputStack = new LinkedList(); + private List collectedParameters = new ArrayList(); + + public List getCollectedParameters() { + return collectedParameters; + } + protected void out(String s) { writer.clause( s ); } @@ -52,6 +62,18 @@ public class SqlGenerator extends SqlGeneratorBase implements ErrorReporter { else { super.out( n ); } + + if ( n instanceof ParameterNode ) { + collectedParameters.add( ( ( ParameterNode ) n ).getHqlParameterSpecification() ); + } + else if ( n instanceof ParameterContainer ) { + if ( ( ( ParameterContainer ) n ).hasEmbeddedParameters() ) { + ParameterSpecification[] specifications = ( ( ParameterContainer ) n ).getEmbeddedParameters(); + if ( specifications != null ) { + collectedParameters.addAll( Arrays.asList( specifications ) ); + } + } + } } protected void commaBetweenParameters(String comma) { diff --git a/src/org/hibernate/hql/ast/exec/AbstractStatementExecutor.java b/src/org/hibernate/hql/ast/exec/AbstractStatementExecutor.java index 1be0071679..bb53e01424 100644 --- a/src/org/hibernate/hql/ast/exec/AbstractStatementExecutor.java +++ b/src/org/hibernate/hql/ast/exec/AbstractStatementExecutor.java @@ -4,6 +4,8 @@ package org.hibernate.hql.ast.exec; import java.sql.PreparedStatement; import java.sql.Connection; import java.sql.Statement; +import java.util.Collections; +import java.util.List; import org.hibernate.HibernateException; import org.hibernate.action.BulkOperationCleanupAction; @@ -34,6 +36,7 @@ public abstract class AbstractStatementExecutor implements StatementExecutor { private final Log log; private final HqlSqlWalker walker; + private List idSelectParameterSpecifications = Collections.EMPTY_LIST; public AbstractStatementExecutor(HqlSqlWalker walker, Log log) { this.walker = walker; @@ -48,6 +51,10 @@ public abstract class AbstractStatementExecutor implements StatementExecutor { return walker.getSessionFactoryHelper().getFactory(); } + protected List getIdSelectParameterSpecifications() { + return idSelectParameterSpecifications; + } + protected abstract Queryable[] getAffectedQueryables(); protected String generateIdInsertSelect(Queryable persister, String tableAlias, AST whereClause) { @@ -80,6 +87,7 @@ public abstract class AbstractStatementExecutor implements StatementExecutor { SqlGenerator sqlGenerator = new SqlGenerator( getFactory() ); sqlGenerator.whereClause( whereClause ); userWhereClause = sqlGenerator.getSQL().substring( 7 ); // strip the " where " + idSelectParameterSpecifications = sqlGenerator.getCollectedParameters(); } catch ( RecognitionException e ) { throw new HibernateException( "Unable to generate id select for DML operation", e ); diff --git a/src/org/hibernate/hql/ast/exec/BasicExecutor.java b/src/org/hibernate/hql/ast/exec/BasicExecutor.java index a3afbdf038..49998257cd 100644 --- a/src/org/hibernate/hql/ast/exec/BasicExecutor.java +++ b/src/org/hibernate/hql/ast/exec/BasicExecutor.java @@ -4,6 +4,7 @@ package org.hibernate.hql.ast.exec; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Iterator; +import java.util.List; import org.hibernate.HibernateException; import org.hibernate.engine.QueryParameters; @@ -31,6 +32,7 @@ public class BasicExecutor extends AbstractStatementExecutor { private final Queryable persister; private final String sql; + private final List parameterSpecifications; public BasicExecutor(HqlSqlWalker walker, Queryable persister) { super( walker, log ); @@ -40,6 +42,7 @@ public class BasicExecutor extends AbstractStatementExecutor { gen.statement( walker.getAST() ); sql = gen.getSQL(); gen.getParseErrorHandler().throwQueryException(); + parameterSpecifications = gen.getCollectedParameters(); } catch ( RecognitionException e ) { throw QuerySyntaxException.convert( e ); @@ -60,10 +63,10 @@ public class BasicExecutor extends AbstractStatementExecutor { try { try { st = session.getBatcher().prepareStatement( sql ); - Iterator paramSpecifications = getWalker().getParameters().iterator(); + Iterator parameterSpecifications = this.parameterSpecifications.iterator(); int pos = 1; - while ( paramSpecifications.hasNext() ) { - final ParameterSpecification paramSpec = ( ParameterSpecification ) paramSpecifications.next(); + while ( parameterSpecifications.hasNext() ) { + final ParameterSpecification paramSpec = ( ParameterSpecification ) parameterSpecifications.next(); pos += paramSpec.bind( st, parameters, session, pos ); } if ( selection != null ) { diff --git a/src/org/hibernate/hql/ast/exec/MultiTableDeleteExecutor.java b/src/org/hibernate/hql/ast/exec/MultiTableDeleteExecutor.java index 9c83539081..4845bd2431 100644 --- a/src/org/hibernate/hql/ast/exec/MultiTableDeleteExecutor.java +++ b/src/org/hibernate/hql/ast/exec/MultiTableDeleteExecutor.java @@ -84,7 +84,7 @@ public class MultiTableDeleteExecutor extends AbstractStatementExecutor { try { try { ps = session.getBatcher().prepareStatement( idInsertSelect ); - Iterator paramSpecifications = getWalker().getParameters().iterator(); + Iterator paramSpecifications = getIdSelectParameterSpecifications().iterator(); int pos = 1; while ( paramSpecifications.hasNext() ) { final ParameterSpecification paramSpec = ( ParameterSpecification ) paramSpecifications.next(); diff --git a/src/org/hibernate/hql/ast/exec/MultiTableUpdateExecutor.java b/src/org/hibernate/hql/ast/exec/MultiTableUpdateExecutor.java index 63319ca9a7..4430e0c65d 100644 --- a/src/org/hibernate/hql/ast/exec/MultiTableUpdateExecutor.java +++ b/src/org/hibernate/hql/ast/exec/MultiTableUpdateExecutor.java @@ -109,7 +109,7 @@ public class MultiTableUpdateExecutor extends AbstractStatementExecutor { try { ps = session.getBatcher().prepareStatement( idInsertSelect ); int parameterStart = getWalker().getNumberOfParametersInSetClause(); - List allParams = getWalker().getParameters(); + List allParams = getIdSelectParameterSpecifications(); Iterator whereParams = allParams.subList( parameterStart, allParams.size() ).iterator(); int sum = 1; // jdbc params are 1-based while ( whereParams.hasNext() ) { diff --git a/src/org/hibernate/hql/ast/tree/FromElement.java b/src/org/hibernate/hql/ast/tree/FromElement.java index 852d26cc0d..60b0dfdf3b 100644 --- a/src/org/hibernate/hql/ast/tree/FromElement.java +++ b/src/org/hibernate/hql/ast/tree/FromElement.java @@ -3,8 +3,10 @@ package org.hibernate.hql.ast.tree; import java.util.LinkedList; import java.util.List; +import java.util.ArrayList; import org.hibernate.QueryException; +import org.hibernate.param.ParameterSpecification; import org.hibernate.engine.JoinSequence; import org.hibernate.hql.QueryTranslator; import org.hibernate.hql.CollectionProperties; @@ -36,7 +38,7 @@ import org.apache.commons.logging.LogFactory; * Date: Dec 6, 2003
* Time: 10:28:17 AM
*/ -public class FromElement extends HqlSqlWalkerNode implements DisplayableNode { +public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, ParameterContainer { private static final Log log = LogFactory.getLog( FromElement.class ); private String className; @@ -548,4 +550,23 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode { public boolean isDereferencedBySubclassProperty() { return dereferencedBySubclassProperty; } + + + // ParameterContainer impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + private List embeddedParameters; + + public void addEmbeddedParameter(ParameterSpecification specification) { + if ( embeddedParameters == null ) { + embeddedParameters = new ArrayList(); + } + embeddedParameters.add( specification ); + } + + public boolean hasEmbeddedParameters() { + return embeddedParameters != null && ! embeddedParameters.isEmpty(); + } + + public ParameterSpecification[] getEmbeddedParameters() { + return ( ParameterSpecification[] ) embeddedParameters.toArray( new ParameterSpecification[ embeddedParameters.size() ] ); + } } diff --git a/src/org/hibernate/hql/ast/tree/ParameterContainer.java b/src/org/hibernate/hql/ast/tree/ParameterContainer.java new file mode 100644 index 0000000000..c3160dd07f --- /dev/null +++ b/src/org/hibernate/hql/ast/tree/ParameterContainer.java @@ -0,0 +1,77 @@ +/* + * 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.hql.ast.tree; + +import org.hibernate.param.ParameterSpecification; + +/** + * Currently this is needed in order to deal with {@link FromElement FromElements} which + * conatin "hidden" JDBC parameters from applying filters. + *

+ * Would love for this to go away, but that would require that Hibernate's + * internal {@link org.hibernate.engine.JoinSequence join handling} be able to either:

+ *

+ * In the interim, this allows us to at least treat these "hidden" parameters properly which is + * the most pressing need. + * + * @deprecated + * @author Steve Ebersole + */ +public interface ParameterContainer { + /** + * Set the renderable text of this node. + * + * @param text The renderable text + */ + public void setText(String text); + + /** + * Adds a parameter specification for a parameter encountered within this node. We use the term 'embedded' here + * because of the fact that the parameter was simply encountered as part of the node's text; it does not exist + * as part of a subtree as it might in a true AST. + * + * @param specification The generated specification. + */ + public void addEmbeddedParameter(ParameterSpecification specification); + + /** + * Determine whether this node contans embedded parameters. The implication is that + * {@link #getEmbeddedParameters()} is allowed to return null if this method returns false. + * + * @return True if this node contains embedded parameters; false otherwise. + */ + public boolean hasEmbeddedParameters(); + + /** + * Retrieve all embedded parameter specifications. + * + * @return All embedded parameter specifications; may return null. + * @see #hasEmbeddedParameters() + */ + public ParameterSpecification[] getEmbeddedParameters(); +} diff --git a/src/org/hibernate/hql/ast/tree/SqlFragment.java b/src/org/hibernate/hql/ast/tree/SqlFragment.java index c0e88d1482..49b4fba7af 100644 --- a/src/org/hibernate/hql/ast/tree/SqlFragment.java +++ b/src/org/hibernate/hql/ast/tree/SqlFragment.java @@ -1,14 +1,41 @@ -// $Id$ +/* + * 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.hql.ast.tree; +import java.util.List; +import java.util.ArrayList; + import org.hibernate.sql.JoinFragment; +import org.hibernate.param.ParameterSpecification; /** * Represents an SQL fragment in the AST. * - * @author josh Dec 5, 2004 9:01:52 AM + * @author josh */ -public class SqlFragment extends Node { +public class SqlFragment extends Node implements ParameterContainer { private JoinFragment joinFragment; private FromElement fromElement; @@ -27,4 +54,23 @@ public class SqlFragment extends Node { public FromElement getFromElement() { return fromElement; } + + + // ParameterContainer impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + private List embeddedParameters; + + public void addEmbeddedParameter(ParameterSpecification specification) { + if ( embeddedParameters == null ) { + embeddedParameters = new ArrayList(); + } + embeddedParameters.add( specification ); + } + + public boolean hasEmbeddedParameters() { + return embeddedParameters != null && ! embeddedParameters.isEmpty(); + } + + public ParameterSpecification[] getEmbeddedParameters() { + return ( ParameterSpecification[] ) embeddedParameters.toArray( new ParameterSpecification[ embeddedParameters.size() ] ); + } } diff --git a/src/org/hibernate/hql/ast/util/JoinProcessor.java b/src/org/hibernate/hql/ast/util/JoinProcessor.java index 78116856ac..5abd0927ca 100644 --- a/src/org/hibernate/hql/ast/util/JoinProcessor.java +++ b/src/org/hibernate/hql/ast/util/JoinProcessor.java @@ -4,21 +4,28 @@ package org.hibernate.hql.ast.util; import java.util.ArrayList; import java.util.Iterator; import java.util.ListIterator; -import java.util.Collections; import java.util.List; +import java.util.StringTokenizer; +import java.util.Collection; import org.hibernate.AssertionFailure; +import org.hibernate.param.DynamicFilterParameterSpecification; +import org.hibernate.type.Type; +import org.hibernate.impl.FilterImpl; +import org.hibernate.dialect.Dialect; import org.hibernate.engine.JoinSequence; +import org.hibernate.engine.QueryParameters; import org.hibernate.hql.antlr.SqlTokenTypes; -import org.hibernate.hql.ast.QueryTranslatorImpl; +import org.hibernate.hql.ast.HqlSqlWalker; import org.hibernate.hql.ast.tree.FromClause; import org.hibernate.hql.ast.tree.FromElement; import org.hibernate.hql.ast.tree.QueryNode; import org.hibernate.hql.ast.tree.DotNode; +import org.hibernate.hql.ast.tree.ParameterContainer; +import org.hibernate.hql.classic.ParserHelper; import org.hibernate.sql.JoinFragment; import org.hibernate.util.StringHelper; - -import antlr.ASTFactory; +import org.hibernate.util.ArrayHelper; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -34,18 +41,17 @@ public class JoinProcessor implements SqlTokenTypes { private static final Log log = LogFactory.getLog( JoinProcessor.class ); - private QueryTranslatorImpl queryTranslatorImpl; - private SyntheticAndFactory andFactory; + private final HqlSqlWalker walker; + private final SyntheticAndFactory syntheticAndFactory; /** * Constructs a new JoinProcessor. * - * @param astFactory The factory for AST node creation. - * @param queryTranslatorImpl The query translator. + * @param walker The walker to which we are bound, giving us access to needed resources. */ - public JoinProcessor(ASTFactory astFactory, QueryTranslatorImpl queryTranslatorImpl) { - this.andFactory = new SyntheticAndFactory( astFactory ); - this.queryTranslatorImpl = queryTranslatorImpl; + public JoinProcessor(HqlSqlWalker walker) { + this.walker = walker; + this.syntheticAndFactory = new SyntheticAndFactory( walker ); } /** @@ -69,7 +75,7 @@ public class JoinProcessor implements SqlTokenTypes { } } - public void processJoins(QueryNode query, boolean inSubquery) { + public void processJoins(QueryNode query) { final FromClause fromClause = query.getFromClause(); final List fromElements; @@ -108,22 +114,21 @@ public class JoinProcessor implements SqlTokenTypes { log.trace( "forcing inclusion of extra joins [alias=" + alias + ", containsTableAlias=" + containsTableAlias + "]" ); return true; } - boolean shallowQuery = queryTranslatorImpl.isShallowQuery(); + boolean shallowQuery = walker.isShallowQuery(); boolean includeSubclasses = fromElement.isIncludeSubclasses(); boolean subQuery = fromClause.isSubQuery(); return includeSubclasses && containsTableAlias && !subQuery && !shallowQuery; } } ); - addJoinNodes( query, join, fromElement, inSubquery ); + addJoinNodes( query, join, fromElement ); } } - private void addJoinNodes(QueryNode query, JoinSequence join, FromElement fromElement, boolean inSubquery) { - // Generate FROM and WHERE fragments for the from element. + private void addJoinNodes(QueryNode query, JoinSequence join, FromElement fromElement) { JoinFragment joinFragment = join.toJoinFragment( - inSubquery ? Collections.EMPTY_MAP : queryTranslatorImpl.getEnabledFilters(), + walker.getEnabledFilters(), fromElement.useFromFragment() || fromElement.isDereferencedBySuperclassOrSubclassProperty(), fromElement.getWithClauseFragment(), fromElement.getWithClauseJoinAlias() @@ -143,13 +148,24 @@ public class JoinProcessor implements SqlTokenTypes { // If there is a FROM fragment and the FROM element is an explicit, then add the from part. if ( fromElement.useFromFragment() /*&& StringHelper.isNotEmpty( frag )*/ ) { - String fromFragment = processFromFragment( frag, join ); + String fromFragment = processFromFragment( frag, join ).trim(); if ( log.isDebugEnabled() ) { log.debug( "Using FROM fragment [" + fromFragment + "]" ); } - fromElement.setText( fromFragment.trim() ); // Set the text of the fromElement. + processDynamicFilterParameters( + fromFragment, + fromElement, + walker + ); } - andFactory.addWhereFragment( joinFragment, whereFrag, query, fromElement ); + + syntheticAndFactory.addWhereFragment( + joinFragment, + whereFrag, + query, + fromElement, + walker + ); } private String processFromFragment(String frag, JoinSequence join) { @@ -161,4 +177,56 @@ public class JoinProcessor implements SqlTokenTypes { return fromFragment; } + public static void processDynamicFilterParameters( + final String sqlFragment, + final ParameterContainer container, + final HqlSqlWalker walker) { + if ( walker.getEnabledFilters().isEmpty() + && ( ! hasDynamicFilterParam( sqlFragment ) ) + && ( ! ( hasCollectionFilterParam( sqlFragment ) ) ) ) { + return; + } + + Dialect dialect = walker.getSessionFactoryHelper().getFactory().getDialect(); + String symbols = new StringBuffer().append( ParserHelper.HQL_SEPARATORS ) + .append( dialect.openQuote() ) + .append( dialect.closeQuote() ) + .toString(); + StringTokenizer tokens = new StringTokenizer( sqlFragment, symbols, true ); + StringBuffer result = new StringBuffer(); + + while ( tokens.hasMoreTokens() ) { + final String token = tokens.nextToken(); + if ( token.startsWith( ParserHelper.HQL_VARIABLE_PREFIX ) ) { + final String filterParameterName = token.substring( 1 ); + final String[] parts = QueryParameters.parseFilterParameterName( filterParameterName ); + final FilterImpl filter = ( FilterImpl ) walker.getEnabledFilters().get( parts[0] ); + final Object value = filter.getParameter( parts[1] ); + final Type type = filter.getFilterDefinition().getParameterType( parts[1] ); + final String typeBindFragment = StringHelper.join( + ",", + ArrayHelper.fillArray( "?", type.getColumnSpan( walker.getSessionFactoryHelper().getFactory() ) ) + ); + final String bindFragment = ( value != null && Collection.class.isInstance( value ) ) + ? StringHelper.join( ",", ArrayHelper.fillArray( typeBindFragment, ( ( Collection ) value ).size() ) ) + : typeBindFragment; + result.append( bindFragment ); + container.addEmbeddedParameter( new DynamicFilterParameterSpecification( parts[0], parts[1], type ) ); + } + else { + result.append( token ); + } + } + + container.setText( result.toString() ); + } + + private static boolean hasDynamicFilterParam(String sqlFragment) { + return sqlFragment.indexOf( ParserHelper.HQL_VARIABLE_PREFIX ) < 0; + } + + private static boolean hasCollectionFilterParam(String sqlFragment) { + return sqlFragment.indexOf( "?" ) < 0; + } + } diff --git a/src/org/hibernate/hql/ast/util/SyntheticAndFactory.java b/src/org/hibernate/hql/ast/util/SyntheticAndFactory.java index 11eb7db735..2d2e2038d4 100644 --- a/src/org/hibernate/hql/ast/util/SyntheticAndFactory.java +++ b/src/org/hibernate/hql/ast/util/SyntheticAndFactory.java @@ -2,18 +2,20 @@ package org.hibernate.hql.ast.util; import java.util.Map; -import java.util.StringTokenizer; import org.hibernate.hql.antlr.HqlSqlTokenTypes; import org.hibernate.hql.ast.tree.FromElement; import org.hibernate.hql.ast.tree.QueryNode; import org.hibernate.hql.ast.tree.RestrictableStatement; import org.hibernate.hql.ast.tree.SqlFragment; +import org.hibernate.hql.ast.tree.Node; +import org.hibernate.hql.ast.HqlSqlWalker; import org.hibernate.persister.entity.Queryable; import org.hibernate.sql.JoinFragment; import org.hibernate.util.StringHelper; +import org.hibernate.type.Type; +import org.hibernate.param.CollectionFilterKeyParameterSpecification; -import antlr.ASTFactory; import antlr.collections.AST; import org.apache.commons.logging.Log; @@ -27,27 +29,36 @@ import org.apache.commons.logging.LogFactory; public class SyntheticAndFactory implements HqlSqlTokenTypes { private static final Log log = LogFactory.getLog( SyntheticAndFactory.class ); - private ASTFactory astFactory; + private HqlSqlWalker hqlSqlWalker; private AST thetaJoins; private AST filters; - public SyntheticAndFactory(ASTFactory astFactory) { - this.astFactory = astFactory; + public SyntheticAndFactory(HqlSqlWalker hqlSqlWalker) { + this.hqlSqlWalker = hqlSqlWalker; } - public void addWhereFragment(JoinFragment joinFragment, String whereFragment, QueryNode query, FromElement fromElement) { + private Node create(int tokenType, String text) { + return ( Node ) ASTUtil.create( hqlSqlWalker.getASTFactory(), tokenType, text ); + } + public void addWhereFragment( + JoinFragment joinFragment, + String whereFragment, + QueryNode query, + FromElement fromElement, + HqlSqlWalker hqlSqlWalker) { if ( whereFragment == null ) { return; } + if ( !fromElement.useWhereFragment() && !joinFragment.hasThetaJoins() ) { + return; + } + whereFragment = whereFragment.trim(); if ( StringHelper.isEmpty( whereFragment ) ) { return; } - else if ( !fromElement.useWhereFragment() && !joinFragment.hasThetaJoins() ) { - return; - } // Forcefully remove leading ands from where fragments; the grammar will // handle adding them @@ -55,12 +66,36 @@ public class SyntheticAndFactory implements HqlSqlTokenTypes { whereFragment = whereFragment.substring( 4 ); } - if ( log.isDebugEnabled() ) log.debug( "Using WHERE fragment [" + whereFragment + "]" ); + if ( log.isDebugEnabled() ) { + log.debug( "Using unprocessed WHERE-fragment [" + whereFragment + "]"); + } - SqlFragment fragment = ( SqlFragment ) ASTUtil.create( astFactory, SQL_TOKEN, whereFragment ); + SqlFragment fragment = ( SqlFragment ) create( SQL_TOKEN, whereFragment ); fragment.setJoinFragment( joinFragment ); fragment.setFromElement( fromElement ); + if ( hqlSqlWalker.isFilter() ) { + if ( whereFragment.indexOf( '?' ) >= 0 ) { + Type collectionFilterKeyType = hqlSqlWalker.getSessionFactoryHelper() + .requireQueryableCollection( hqlSqlWalker.getCollectionFilterRole() ) + .getKeyType(); + CollectionFilterKeyParameterSpecification paramSpec = new CollectionFilterKeyParameterSpecification( + hqlSqlWalker.getCollectionFilterRole(), + collectionFilterKeyType, + 0 + ); + fragment.addEmbeddedParameter( paramSpec ); + } + } + + JoinProcessor.processDynamicFilterParameters( + whereFragment, + fragment, + hqlSqlWalker + ); + + log.debug( "Using processed WHERE-fragment [" + fragment.getText() + "]" ); + // Filter conditions need to be inserted before the HQL where condition and the // theta join node. This is because org.hibernate.loader.Loader binds the filter parameters first, // then it binds all the HQL query parameters, see org.hibernate.loader.Loader.processFilterParameters(). @@ -69,11 +104,11 @@ public class SyntheticAndFactory implements HqlSqlTokenTypes { // Find or create the WHERE clause AST where = query.getWhereClause(); // Create a new FILTERS node as a parent of all filters - filters = astFactory.create( FILTERS, "{filter conditions}" ); + filters = create( FILTERS, "{filter conditions}" ); // Put the FILTERS node before the HQL condition and theta joins ASTUtil.insertChild( where, filters ); } - + // add the current fragment to the FILTERS node filters.addChild( fragment ); } @@ -82,7 +117,7 @@ public class SyntheticAndFactory implements HqlSqlTokenTypes { // Find or create the WHERE clause AST where = query.getWhereClause(); // Create a new THETA_JOINS node as a parent of all filters - thetaJoins = astFactory.create( THETA_JOINS, "{theta joins}" ); + thetaJoins = create( THETA_JOINS, "{theta joins}" ); // Put the THETA_JOINS node before the HQL condition, after the filters. if (filters==null) { ASTUtil.insertChild( where, thetaJoins ); @@ -91,14 +126,18 @@ public class SyntheticAndFactory implements HqlSqlTokenTypes { ASTUtil.insertSibling( thetaJoins, filters ); } } - + // add the current fragment to the THETA_JOINS node thetaJoins.addChild(fragment); } } - public void addDiscriminatorWhereFragment(RestrictableStatement statement, Queryable persister, Map enabledFilters, String alias) { + public void addDiscriminatorWhereFragment( + RestrictableStatement statement, + Queryable persister, + Map enabledFilters, + String alias) { String whereFragment = persister.filterFragment( alias, enabledFilters ).trim(); if ( "".equals( whereFragment ) ) { return; @@ -118,13 +157,19 @@ public class SyntheticAndFactory implements HqlSqlTokenTypes { // At some point we probably want to apply an additional grammar to // properly tokenize this where fragment into constituent parts // focused on the operators embedded within the fragment. - AST discrimNode = astFactory.create( SQL_TOKEN, whereFragment ); + SqlFragment discrimNode = ( SqlFragment ) create( SQL_TOKEN, whereFragment ); + + JoinProcessor.processDynamicFilterParameters( + whereFragment, + discrimNode, + hqlSqlWalker + ); if ( statement.getWhereClause().getNumberOfChildren() == 0 ) { statement.getWhereClause().setFirstChild( discrimNode ); } else { - AST and = astFactory.create( AND, "{and}" ); + AST and = create( AND, "{and}" ); AST currentFirstChild = statement.getWhereClause().getFirstChild(); and.setFirstChild( discrimNode ); and.addChild( currentFirstChild ); diff --git a/src/org/hibernate/loader/hql/QueryLoader.java b/src/org/hibernate/loader/hql/QueryLoader.java index 8cd8b18460..5ca752634a 100644 --- a/src/org/hibernate/loader/hql/QueryLoader.java +++ b/src/org/hibernate/loader/hql/QueryLoader.java @@ -483,8 +483,8 @@ public class QueryLoader extends BasicLoader { final QueryParameters queryParameters, final int startIndex, final SessionImplementor session) throws SQLException { - int position = bindFilterParameterValues( statement, queryParameters, startIndex, session ); - List parameterSpecs = queryTranslator.getSqlAST().getWalker().getParameters(); + int position = startIndex; + List parameterSpecs = queryTranslator.getCollectedParameterSpecifications(); Iterator itr = parameterSpecs.iterator(); while ( itr.hasNext() ) { ParameterSpecification spec = ( ParameterSpecification ) itr.next(); diff --git a/src/org/hibernate/param/CollectionFilterKeyParameterSpecification.java b/src/org/hibernate/param/CollectionFilterKeyParameterSpecification.java index 10ff83d35e..2ad9ea13b2 100644 --- a/src/org/hibernate/param/CollectionFilterKeyParameterSpecification.java +++ b/src/org/hibernate/param/CollectionFilterKeyParameterSpecification.java @@ -8,13 +8,12 @@ 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. + * 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; @@ -34,6 +33,9 @@ public class CollectionFilterKeyParameterSpecification implements ParameterSpeci this.queryParameterPosition = queryParameterPosition; } + /** + * {@inheritDoc} + */ public int bind( PreparedStatement statement, QueryParameters qp, @@ -44,14 +46,23 @@ public class CollectionFilterKeyParameterSpecification implements ParameterSpeci return keyType.getColumnSpan( session.getFactory() ); } + /** + * {@inheritDoc} + */ public Type getExpectedType() { return keyType; } + /** + * {@inheritDoc} + */ public void setExpectedType(Type expectedType) { // todo : throw exception? } + /** + * {@inheritDoc} + */ public String renderDisplayInfo() { return "collection-filter-key=" + collectionRole; } diff --git a/src/org/hibernate/param/DynamicFilterParameterSpecification.java b/src/org/hibernate/param/DynamicFilterParameterSpecification.java index 8bf9cc3926..44688ee0ab 100644 --- a/src/org/hibernate/param/DynamicFilterParameterSpecification.java +++ b/src/org/hibernate/param/DynamicFilterParameterSpecification.java @@ -2,20 +2,17 @@ package org.hibernate.param; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.util.Collection; +import java.util.Iterator; 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. - *

- * 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. + * A specialized ParameterSpecification impl for dealing with a dynamic filter parameters. + * + * @see org.hibernate.Session#enableFilter(String) * * @author Steve Ebersole */ @@ -23,37 +20,65 @@ public class DynamicFilterParameterSpecification implements ParameterSpecificati private final String filterName; private final String parameterName; private final Type definedParameterType; - private final int queryParameterPosition; + /** + * Constructs a parameter specification for a particular filter parameter. + * + * @param filterName The name of the filter + * @param parameterName The name of the parameter + * @param definedParameterType The paremeter type specified on the filter metadata + */ public DynamicFilterParameterSpecification( String filterName, String parameterName, - Type definedParameterType, - int queryParameterPosition) { + Type definedParameterType) { this.filterName = filterName; this.parameterName = parameterName; this.definedParameterType = definedParameterType; - this.queryParameterPosition = queryParameterPosition; } + /** + * {@inheritDoc} + */ 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() ); + int start) throws SQLException { + final int columnSpan = definedParameterType.getColumnSpan( session.getFactory() ); + final Object value = session.getFilterParameterValue( filterName + '.' + parameterName ); + if ( Collection.class.isInstance( value ) ) { + int positions = 0; + Iterator itr = ( ( Collection ) value ).iterator(); + while ( itr.hasNext() ) { + definedParameterType.nullSafeSet( statement, itr.next(), start + positions, session ); + positions += columnSpan; + } + return positions; + } + else { + definedParameterType.nullSafeSet( statement, value, start, session ); + return columnSpan; + } } + /** + * {@inheritDoc} + */ public Type getExpectedType() { return definedParameterType; } + /** + * {@inheritDoc} + */ public void setExpectedType(Type expectedType) { - // todo : throw exception? + // todo : throw exception? maybe warn if not the same? } + /** + * {@inheritDoc} + */ public String renderDisplayInfo() { return "dynamic-filter={filterName=" + filterName + ",paramName=" + parameterName + "}"; } diff --git a/src/org/hibernate/param/NamedParameterSpecification.java b/src/org/hibernate/param/NamedParameterSpecification.java index a53d300ae7..043a0c075f 100644 --- a/src/org/hibernate/param/NamedParameterSpecification.java +++ b/src/org/hibernate/param/NamedParameterSpecification.java @@ -9,14 +9,20 @@ import java.sql.PreparedStatement; import java.sql.SQLException; /** - * Relates to an explicit query named-parameter. + * Parameter bind specification for an explicit named parameter. * * @author Steve Ebersole */ public class NamedParameterSpecification extends AbstractExplicitParameterSpecification implements ParameterSpecification { - private final String name; + /** + * Constructs a named parameter bind specification. + * + * @param sourceLine See {@link #getSourceLine()} + * @param sourceColumn See {@link #getSourceColumn()} + * @param name The named parameter name. + */ public NamedParameterSpecification(int sourceLine, int sourceColumn, String name) { super( sourceLine, sourceColumn ); this.name = name; @@ -39,10 +45,18 @@ public class NamedParameterSpecification extends AbstractExplicitParameterSpecif return typedValue.getType().getColumnSpan( session.getFactory() ); } + /** + * {@inheritDoc} + */ public String renderDisplayInfo() { return "name=" + name + ", expectedType=" + getExpectedType(); } + /** + * Getter for property 'name'. + * + * @return Value for property 'name'. + */ public String getName() { return name; } diff --git a/src/org/hibernate/param/PositionalParameterSpecification.java b/src/org/hibernate/param/PositionalParameterSpecification.java index 226b3aedc9..0730c29a0b 100644 --- a/src/org/hibernate/param/PositionalParameterSpecification.java +++ b/src/org/hibernate/param/PositionalParameterSpecification.java @@ -9,14 +9,20 @@ import java.sql.PreparedStatement; import java.sql.SQLException; /** - * Relates to an explicit query positional (or ordinal) parameter. + * Parameter bind specification for an explicit positional (or ordinal) parameter. * * @author Steve Ebersole */ public class PositionalParameterSpecification extends AbstractExplicitParameterSpecification implements ParameterSpecification { - private final int hqlPosition; + /** + * Constructs a position/ordinal parameter bind specification. + * + * @param sourceLine See {@link #getSourceLine()} + * @param sourceColumn See {@link #getSourceColumn()} + * @param hqlPosition The position in the source query, relative to the other source positional parameters. + */ public PositionalParameterSpecification(int sourceLine, int sourceColumn, int hqlPosition) { super( sourceLine, sourceColumn ); this.hqlPosition = hqlPosition; @@ -40,10 +46,18 @@ public class PositionalParameterSpecification extends AbstractExplicitParameterS return type.getColumnSpan( session.getFactory() ); } + /** + * {@inheritDoc} + */ public String renderDisplayInfo() { return "ordinal=" + hqlPosition + ", expectedType=" + getExpectedType(); } + /** + * Getter for property 'hqlPosition'. + * + * @return Value for property 'hqlPosition'. + */ public int getHqlPosition() { return hqlPosition; } diff --git a/src/org/hibernate/param/VersionTypeSeedParameterSpecification.java b/src/org/hibernate/param/VersionTypeSeedParameterSpecification.java index 51084c83e2..1bf9d66c85 100644 --- a/src/org/hibernate/param/VersionTypeSeedParameterSpecification.java +++ b/src/org/hibernate/param/VersionTypeSeedParameterSpecification.java @@ -11,20 +11,24 @@ import java.sql.PreparedStatement; import java.sql.SQLException; /** - * Implementation of VersionTypeSeedParameterSpecification. + * Parameter bind specification used for optimisitc lock version seeding (from insert statements). * * @author Steve Ebersole */ public class VersionTypeSeedParameterSpecification implements ParameterSpecification { - private VersionType type; + /** + * Constructs a version seed parameter bind specification. + * + * @param type The version type. + */ public VersionTypeSeedParameterSpecification(VersionType type) { this.type = type; } /** - * @see org.hibernate.param.ParameterSpecification#bind + * {@inheritDoc} */ public int bind(PreparedStatement statement, QueryParameters qp, SessionImplementor session, int position) throws SQLException { @@ -32,14 +36,23 @@ public class VersionTypeSeedParameterSpecification implements ParameterSpecifica return 1; } + /** + * {@inheritDoc} + */ public Type getExpectedType() { return type; } + /** + * {@inheritDoc} + */ public void setExpectedType(Type expectedType) { // expected type is intrinsic here... } + /** + * {@inheritDoc} + */ public String renderDisplayInfo() { return "version-seed, type=" + type; } diff --git a/test/org/hibernate/test/filter/DynamicFilterTest.java b/test/org/hibernate/test/filter/DynamicFilterTest.java index cd27fca8f0..a06e42604f 100644 --- a/test/org/hibernate/test/filter/DynamicFilterTest.java +++ b/test/org/hibernate/test/filter/DynamicFilterTest.java @@ -10,26 +10,26 @@ import java.util.List; import java.util.Set; import junit.framework.Test; -import junit.framework.TestSuite; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.hibernate.EntityMode; import org.hibernate.FetchMode; import org.hibernate.Hibernate; import org.hibernate.Session; import org.hibernate.Transaction; -import org.hibernate.junit.functional.FunctionalTestCase; -import org.hibernate.junit.functional.FunctionalTestClassTestSuite; +import org.hibernate.Criteria; +import org.hibernate.impl.SessionFactoryImpl; +import org.hibernate.engine.SessionImplementor; import org.hibernate.cache.CacheKey; import org.hibernate.cache.entry.CollectionCacheEntry; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; -import org.hibernate.criterion.Expression; -import org.hibernate.engine.SessionFactoryImplementor; -import org.hibernate.impl.SessionFactoryImpl; +import org.hibernate.criterion.Restrictions; +import org.hibernate.criterion.DetachedCriteria; +import org.hibernate.criterion.Property; +import org.hibernate.criterion.Subqueries; +import org.hibernate.junit.functional.FunctionalTestCase; +import org.hibernate.junit.functional.FunctionalTestClassTestSuite; import org.hibernate.persister.collection.CollectionPersister; -import org.hibernate.test.TestCase; import org.hibernate.transform.DistinctRootEntityResultTransformer; /** @@ -39,8 +39,6 @@ import org.hibernate.transform.DistinctRootEntityResultTransformer; */ public class DynamicFilterTest extends FunctionalTestCase { - private Log log = LogFactory.getLog( DynamicFilterTest.class ); - public DynamicFilterTest(String testName) { super( testName ); } @@ -79,6 +77,7 @@ public class DynamicFilterTest extends FunctionalTestCase { testData.prepare(); Session session = openSession(); + long ts = ( ( SessionImplementor ) session ).getTimestamp(); // Force a collection into the second level cache, with its non-filtered elements Salesperson sp = ( Salesperson ) session.load( Salesperson.class, testData.steveId ); @@ -93,6 +92,7 @@ public class DynamicFilterTest extends FunctionalTestCase { session.close(); session = openSession(); + ts = ( ( SessionImplementor ) session ).getTimestamp(); session.enableFilter( "fulfilledOrders" ).setParameter( "asOfDate", testData.lastMonth.getTime() ); sp = ( Salesperson ) session.createQuery( "from Salesperson as s where s.id = :id" ) .setLong( "id", testData.steveId.longValue() ) @@ -152,7 +152,6 @@ public class DynamicFilterTest extends FunctionalTestCase { //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // HQL test //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - log.info( "Starting HQL filter tests" ); TestData testData = new TestData(); testData.prepare(); @@ -162,13 +161,11 @@ public class DynamicFilterTest extends FunctionalTestCase { session.enableFilter( "effectiveDate" ) .setParameter( "asOfDate", testData.lastMonth.getTime() ); - log.info( "HQL against Salesperson..." ); List results = session.createQuery( "select s from Salesperson as s left join fetch s.orders" ).list(); assertTrue( "Incorrect filtered HQL result count [" + results.size() + "]", results.size() == 1 ); Salesperson result = ( Salesperson ) results.get( 0 ); assertTrue( "Incorrect collectionfilter count", result.getOrders().size() == 1 ); - log.info( "HQL against Product..." ); results = session.createQuery( "from Product as p where p.stockNumber = ?" ).setInteger( 0, 124 ).list(); assertTrue( results.size() == 1 ); @@ -180,7 +177,6 @@ public class DynamicFilterTest extends FunctionalTestCase { //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Criteria-query test //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - log.info( "Starting Criteria-query filter tests" ); TestData testData = new TestData(); testData.prepare(); @@ -193,16 +189,14 @@ public class DynamicFilterTest extends FunctionalTestCase { session.enableFilter( "effectiveDate" ) .setParameter( "asOfDate", testData.lastMonth.getTime() ); - log.info( "Criteria query against Salesperson..." ); List salespersons = session.createCriteria( Salesperson.class ) .setFetchMode( "orders", FetchMode.JOIN ) .list(); assertEquals( "Incorrect salesperson count", 1, salespersons.size() ); assertEquals( "Incorrect order count", 1, ( ( Salesperson ) salespersons.get( 0 ) ).getOrders().size() ); - log.info( "Criteria query against Product..." ); List products = session.createCriteria( Product.class ) - .add( Expression.eq( "stockNumber", new Integer( 124 ) ) ) + .add( Restrictions.eq( "stockNumber", new Integer( 124 ) ) ) .list(); assertEquals( "Incorrect product count", 1, products.size() ); @@ -210,18 +204,292 @@ public class DynamicFilterTest extends FunctionalTestCase { testData.release(); } + public void testCriteriaControl() { + TestData testData = new TestData(); + testData.prepare(); + + // the subquery... + DetachedCriteria subquery = DetachedCriteria.forClass( Salesperson.class ) + .setProjection( Property.forName( "name" ) ); + + Session session = openSession(); + session.beginTransaction(); + session.enableFilter( "fulfilledOrders" ).setParameter( "asOfDate", testData.lastMonth.getTime() ); + session.enableFilter( "regionlist" ).setParameterList( "regions", new String[] { "APAC" } ); + + List result = session.createCriteria( Order.class ) + .add( Subqueries.in( "steve", subquery ) ) + .list(); + assertEquals( 1, result.size() ); + + session.getTransaction().commit(); + session.close(); + + testData.release(); + } + + public void testCriteriaSubqueryWithFilters() { + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Criteria-subquery test + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + TestData testData = new TestData(); + testData.prepare(); + + Session session = openSession(); + session.enableFilter("region").setParameter("region", "APAC"); + + DetachedCriteria salespersonSubquery = DetachedCriteria.forClass(Salesperson.class) + .add(Restrictions.eq("name", "steve")) + .setProjection(Property.forName("department")); + + Criteria departmentsQuery = session.createCriteria(Department.class).add(Subqueries.propertyIn("id", salespersonSubquery)); + List departments = departmentsQuery.list(); + + assertEquals("Incorrect department count", 1, departments.size()); + + session.enableFilter("region").setParameter("region", "Foobar"); + departments = departmentsQuery.list(); + + assertEquals("Incorrect department count", 0, departments.size()); + + session.enableFilter("region").setParameter("region", "APAC"); + + DetachedCriteria lineItemSubquery = DetachedCriteria.forClass(LineItem.class) + .add(Restrictions.ge("quantity", 1L)) + .createCriteria("product") + .add(Restrictions.eq("name", "Acme Hair Gel")) + .setProjection(Property.forName("id")); + + List orders = session.createCriteria(Order.class) + .add(Subqueries.exists(lineItemSubquery)) + .add(Restrictions.eq("buyer", "gavin")) + .list(); + + assertEquals("Incorrect orders count", 1, orders.size()); + + session.enableFilter("region").setParameter("region", "APAC"); + session.enableFilter("effectiveDate").setParameter("asOfDate", testData.lastMonth.getTime()); + + DetachedCriteria productSubquery = DetachedCriteria.forClass(Product.class) + .add(Restrictions.eq("name", "Acme Hair Gel")) + .setProjection(Property.forName("id")); + + lineItemSubquery = DetachedCriteria.forClass(LineItem.class) + .add(Restrictions.ge("quantity", 1L)) + .createCriteria("product") + .add(Subqueries.propertyIn("id", productSubquery)) + .setProjection(Property.forName("id")); + + orders = session.createCriteria(Order.class) + .add(Subqueries.exists(lineItemSubquery)) + .add(Restrictions.eq("buyer", "gavin")) + .list(); + + assertEquals("Incorrect orders count", 1, orders.size()); + + session.enableFilter("region").setParameter("region", "APAC"); + session.enableFilter("effectiveDate").setParameter("asOfDate", testData.fourMonthsAgo.getTime()); + + orders = session.createCriteria(Order.class) + .add(Subqueries.exists(lineItemSubquery)) + .add(Restrictions.eq("buyer", "gavin")) + .list(); + + assertEquals("Incorrect orders count", 0, orders.size()); + + session.close(); + testData.release(); + } + + public void testHQLSubqueryWithFilters() { + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // HQL subquery with filters test + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + TestData testData = new TestData(); + testData.prepare(); + + Session session = openSession(); + session.enableFilter("region").setParameter("region", "APAC"); + + List departments = session.createQuery("select d from Department as d where d.id in (select s.department from Salesperson s where s.name = ?)").setString(0, "steve").list(); + + assertEquals("Incorrect department count", 1, departments.size()); + + session.enableFilter("region").setParameter("region", "Foobar"); + departments = session.createQuery("select d from Department as d where d.id in (select s.department from Salesperson s where s.name = ?)").setString(0, "steve").list(); + + assertEquals("Incorrect department count", 0, departments.size()); + + session.enableFilter("region").setParameter("region", "APAC"); + + List orders = session.createQuery("select o from Order as o where exists (select li.id from LineItem li, Product as p where p.id = li.product and li.quantity >= ? and p.name = ?) and o.buyer = ?") + .setLong(0, 1L).setString(1, "Acme Hair Gel").setString(2, "gavin").list(); + + assertEquals("Incorrect orders count", 1, orders.size()); + + session.enableFilter("region").setParameter("region", "APAC"); + session.enableFilter("effectiveDate").setParameter("asOfDate", testData.lastMonth.getTime()); + + orders = session.createQuery("select o from Order as o where exists (select li.id from LineItem li where li.quantity >= ? and li.product in (select p.id from Product p where p.name = ?)) and o.buyer = ?") + .setLong(0, 1L).setString(1, "Acme Hair Gel").setString(2, "gavin").list(); + + assertEquals("Incorrect orders count", 1, orders.size()); + + session.enableFilter("region").setParameter("region", "APAC"); + session.enableFilter("effectiveDate").setParameter("asOfDate", testData.fourMonthsAgo.getTime()); + + orders = session.createQuery("select o from Order as o where exists (select li.id from LineItem li where li.quantity >= ? and li.product in (select p.id from Product p where p.name = ?)) and o.buyer = ?") + .setLong(0, 1L).setString(1, "Acme Hair Gel").setString(2, "gavin").list(); + + assertEquals("Incorrect orders count", 0, orders.size()); + + session.enableFilter("region").setParameter("region", "APAC"); + session.enableFilter("effectiveDate").setParameter("asOfDate", testData.lastMonth.getTime()); + + orders = session.createQuery("select o from Order as o where exists (select li.id from LineItem li where li.quantity >= :quantity and li.product in (select p.id from Product p where p.name = :name)) and o.buyer = :buyer") + .setLong("quantity", 1L).setString("name", "Acme Hair Gel").setString("buyer", "gavin").list(); + + assertEquals("Incorrect orders count", 1, orders.size()); + + session.enableFilter("region").setParameter("region", "APAC"); + session.enableFilter("effectiveDate").setParameter("asOfDate", testData.lastMonth.getTime()); + + orders = session.createQuery("select o from Order as o where exists (select li.id from LineItem li where li.quantity >= ? and li.product in (select p.id from Product p where p.name = ?)) and o.buyer = :buyer") + .setLong(0, 1L).setString(1, "Acme Hair Gel").setString("buyer", "gavin").list(); + + assertEquals("Incorrect orders count", 1, orders.size()); + + session.close(); + testData.release(); + } + + public void testFilterApplicationOnHqlQueryWithImplicitSubqueryContainingPositionalParameter() { + TestData testData = new TestData(); + testData.prepare(); + + Session session = openSession(); + session.beginTransaction(); + + final String queryString = "from Order o where ? in ( select sp.name from Salesperson sp )"; + + // first a control-group query + List result = session.createQuery( queryString ).setParameter( 0, "steve" ).list(); + assertEquals( 2, result.size() ); + + // now lets enable filters on Order... + session.enableFilter( "fulfilledOrders" ).setParameter( "asOfDate", testData.lastMonth.getTime() ); + result = session.createQuery( queryString ).setParameter( 0, "steve" ).list(); + assertEquals( 1, result.size() ); + + // now, lets additionally enable filter on Salesperson. First a valid one... + session.enableFilter( "regionlist" ).setParameterList( "regions", new String[] { "APAC" } ); + result = session.createQuery( queryString ).setParameter( 0, "steve" ).list(); + assertEquals( 1, result.size() ); + + // ... then a silly one... + session.enableFilter( "regionlist" ).setParameterList( "regions", new String[] { "gamma quadrant" } ); + result = session.createQuery( queryString ).setParameter( 0, "steve" ).list(); + assertEquals( 0, result.size() ); + + session.getTransaction().commit(); + session.close(); + + testData.release(); + } + + public void testFilterApplicationOnHqlQueryWithImplicitSubqueryContainingNamedParameter() { + TestData testData = new TestData(); + testData.prepare(); + + Session session = openSession(); + session.beginTransaction(); + + final String queryString = "from Order o where :salesPersonName in ( select sp.name from Salesperson sp )"; + + // first a control-group query + List result = session.createQuery( queryString ).setParameter( "salesPersonName", "steve" ).list(); + assertEquals( 2, result.size() ); + + // now lets enable filters on Order... + session.enableFilter( "fulfilledOrders" ).setParameter( "asOfDate", testData.lastMonth.getTime() ); + result = session.createQuery( queryString ).setParameter( "salesPersonName", "steve" ).list(); + assertEquals( 1, result.size() ); + + // now, lets additionally enable filter on Salesperson. First a valid one... + session.enableFilter( "regionlist" ).setParameterList( "regions", new String[] { "APAC" } ); + result = session.createQuery( queryString ).setParameter( "salesPersonName", "steve" ).list(); + assertEquals( 1, result.size() ); + + // ... then a silly one... + session.enableFilter( "regionlist" ).setParameterList( "regions", new String[] { "gamma quadrant" } ); + result = session.createQuery( queryString ).setParameter( "salesPersonName", "steve" ).list(); + assertEquals( 0, result.size() ); + + session.getTransaction().commit(); + session.close(); + + testData.release(); + } + + public void testFiltersOnSimpleHqlDelete() { + Session session = openSession(); + session.beginTransaction(); + Salesperson sp = new Salesperson(); + sp.setName( "steve" ); + sp.setRegion( "NA" ); + session.persist( sp ); + Salesperson sp2 = new Salesperson(); + sp2.setName( "john" ); + sp2.setRegion( "APAC" ); + session.persist( sp2 ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + session.enableFilter( "region" ).setParameter( "region", "NA" ); + int count = session.createQuery( "delete from Salesperson" ).executeUpdate(); + assertEquals( 1, count ); + session.delete( sp2 ); + session.getTransaction().commit(); + session.close(); + } + + public void testFiltersOnMultiTableHqlDelete() { + Session session = openSession(); + session.beginTransaction(); + Salesperson sp = new Salesperson(); + sp.setName( "steve" ); + sp.setRegion( "NA" ); + session.persist( sp ); + Salesperson sp2 = new Salesperson(); + sp2.setName( "john" ); + sp2.setRegion( "APAC" ); + session.persist( sp2 ); + session.getTransaction().commit(); + session.close(); + + session = openSession(); + session.beginTransaction(); + session.enableFilter( "region" ).setParameter( "region", "NA" ); + int count = session.createQuery( "delete from Salesperson" ).executeUpdate(); + assertEquals( 1, count ); + session.delete( sp2 ); + session.getTransaction().commit(); + session.close(); + } + + public void testGetFilters() { //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Get() test //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - log.info( "Starting get() filter tests (eager assoc. fetching)." ); TestData testData = new TestData(); testData.prepare(); Session session = openSession(); session.enableFilter( "region" ).setParameter( "region", "APAC" ); - log.info( "Performing get()..." ); Salesperson salesperson = ( Salesperson ) session.get( Salesperson.class, testData.steveId ); assertNotNull( salesperson ); assertEquals( "Incorrect order count", 1, salesperson.getOrders().size() ); @@ -234,7 +502,6 @@ public class DynamicFilterTest extends FunctionalTestCase { //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // one-to-many loading tests //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - log.info( "Starting one-to-many collection loader filter tests." ); TestData testData = new TestData(); testData.prepare(); @@ -242,7 +509,6 @@ public class DynamicFilterTest extends FunctionalTestCase { session.enableFilter( "seniorSalespersons" ) .setParameter( "asOfDate", testData.lastMonth.getTime() ); - log.info( "Performing load of Department..." ); Department department = ( Department ) session.load( Department.class, testData.deptId ); Set salespersons = department.getSalespersons(); assertEquals( "Incorrect salesperson count", 1, salespersons.size() ); @@ -255,7 +521,6 @@ public class DynamicFilterTest extends FunctionalTestCase { //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // one-to-many loading tests //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - log.info( "Starting one-to-many collection loader filter tests." ); TestData testData = new TestData(); testData.prepare(); @@ -263,7 +528,6 @@ public class DynamicFilterTest extends FunctionalTestCase { session.enableFilter( "regionlist" ) .setParameterList( "regions", new String[]{"LA", "APAC"} ); - log.debug( "Performing query of Salespersons" ); List salespersons = session.createQuery( "from Salesperson" ).list(); assertEquals( "Incorrect salesperson count", 1, salespersons.size() ); @@ -280,7 +544,7 @@ public class DynamicFilterTest extends FunctionalTestCase { Product prod = ( Product ) session.createCriteria( Product.class ) .setResultTransformer( new DistinctRootEntityResultTransformer() ) - .add( Expression.eq( "id", testData.prod1Id ) ) + .add( Restrictions.eq( "id", testData.prod1Id ) ) .uniqueResult(); assertNotNull( prod ); @@ -420,7 +684,7 @@ public class DynamicFilterTest extends FunctionalTestCase { Session session = openSession(); List result = session.createCriteria( Product.class ) - .add( Expression.eq( "id", testData.prod1Id ) ) + .add( Restrictions.eq( "id", testData.prod1Id ) ) .list(); Product prod = ( Product ) result.get( 0 ); diff --git a/test/org/hibernate/test/filter/hql/Basic.hbm.xml b/test/org/hibernate/test/filter/hql/Basic.hbm.xml new file mode 100644 index 0000000000..3e6e9e1c29 --- /dev/null +++ b/test/org/hibernate/test/filter/hql/Basic.hbm.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + diff --git a/test/org/hibernate/test/filter/hql/BasicFilteredBulkManipulationTest.java b/test/org/hibernate/test/filter/hql/BasicFilteredBulkManipulationTest.java new file mode 100644 index 0000000000..f24e6ca149 --- /dev/null +++ b/test/org/hibernate/test/filter/hql/BasicFilteredBulkManipulationTest.java @@ -0,0 +1,93 @@ +/* + * 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.test.filter.hql; + +import org.hibernate.junit.functional.FunctionalTestCase; +import org.hibernate.Session; + +/** + * Tests for application of filters + * + * @author Steve Ebersole + */ +public class BasicFilteredBulkManipulationTest extends FunctionalTestCase { + public BasicFilteredBulkManipulationTest(String string) { + super( string ); + } + + public String[] getMappings() { + return new String[]{ + "filter/hql/filter-defs.hbm.xml", + "filter/hql/Basic.hbm.xml" + }; + } + + public void testBasicFilteredHqlDelete() { + Session s = openSession(); + s.beginTransaction(); + s.save( new Person( "Steve", 'M' ) ); + s.save( new Person( "Emmanuel", 'M' ) ); + s.save( new Person( "Gail", 'F' ) ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.beginTransaction(); + s.enableFilter( "sex" ).setParameter( "sexCode", new Character( 'M' ) ); + int count = s.createQuery( "delete Person" ).executeUpdate(); + assertEquals( 2, count ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.beginTransaction(); + s.createQuery( "delete Person" ).executeUpdate(); + s.getTransaction().commit(); + s.close(); + } + + public void testBasicFilteredHqlUpdate() { + Session s = openSession(); + s.beginTransaction(); + s.save( new Person( "Shawn", 'M' ) ); + s.save( new Person( "Sally", 'F' ) ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.beginTransaction(); + s.enableFilter( "sex" ).setParameter( "sexCode", new Character( 'M' ) ); + int count = s.createQuery( "update Person p set p.name = 'Shawn'" ).executeUpdate(); + assertEquals( 1, count ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.beginTransaction(); + s.createQuery( "delete Person" ).executeUpdate(); + s.getTransaction().commit(); + s.close(); + } +} diff --git a/test/org/hibernate/test/filter/hql/Customer.java b/test/org/hibernate/test/filter/hql/Customer.java new file mode 100644 index 0000000000..d2c8fe7e55 --- /dev/null +++ b/test/org/hibernate/test/filter/hql/Customer.java @@ -0,0 +1,51 @@ +/* + * 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.test.filter.hql; + +/** + * Leaf subclass + * + * @author Steve Ebersole + */ +public class Customer extends User { + private String company; + + protected Customer() { + super(); + } + + public Customer(String name, char sex, String username, String company) { + super( name, sex, username ); + this.company = company; + } + + public String getCompany() { + return company; + } + + public void setCompany(String company) { + this.company = company; + } +} diff --git a/test/org/hibernate/test/filter/hql/Employee.java b/test/org/hibernate/test/filter/hql/Employee.java new file mode 100644 index 0000000000..36e041cff6 --- /dev/null +++ b/test/org/hibernate/test/filter/hql/Employee.java @@ -0,0 +1,53 @@ +/* + * 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.test.filter.hql; + +import java.util.Date; + +/** + * Leaf subclass + * + * @author Steve Ebersole + */ +public class Employee extends User { + private Date hireDate; + + protected Employee() { + super(); + } + + public Employee(String name, char sex, String username, Date hireDate) { + super( name, sex, username ); + this.hireDate = hireDate; + } + + public Date getHireDate() { + return hireDate; + } + + public void setHireDate(Date hireDate) { + this.hireDate = hireDate; + } +} diff --git a/test/org/hibernate/test/filter/hql/Joined.hbm.xml b/test/org/hibernate/test/filter/hql/Joined.hbm.xml new file mode 100644 index 0000000000..b3ade68cd4 --- /dev/null +++ b/test/org/hibernate/test/filter/hql/Joined.hbm.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/org/hibernate/test/filter/hql/JoinedFilteredBulkManipulationTest.java b/test/org/hibernate/test/filter/hql/JoinedFilteredBulkManipulationTest.java new file mode 100644 index 0000000000..3bbc7d605d --- /dev/null +++ b/test/org/hibernate/test/filter/hql/JoinedFilteredBulkManipulationTest.java @@ -0,0 +1,201 @@ +/* + * 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.test.filter.hql; + +import java.util.Date; + +import org.hibernate.junit.functional.FunctionalTestCase; +import org.hibernate.Session; + +/** + * TODO : javadoc + * + * @author Steve Ebersole + */ +public class JoinedFilteredBulkManipulationTest extends FunctionalTestCase { + public JoinedFilteredBulkManipulationTest(String string) { + super( string ); + } + + public String[] getMappings() { + return new String[]{ + "filter/hql/filter-defs.hbm.xml", + "filter/hql/Joined.hbm.xml" + }; + } + + public void testFilteredJoinedSubclassHqlDeleteRoot() { + Session s = openSession(); + s.beginTransaction(); + s.save( new Employee( "John", 'M', "john", new Date() ) ); + s.save( new Employee( "Jane", 'F', "jane", new Date() ) ); + s.save( new Customer( "Charlie", 'M', "charlie", "Acme" ) ); + s.save( new Customer( "Wanda", 'F', "wanda", "ABC" ) ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.beginTransaction(); + s.enableFilter( "sex" ).setParameter( "sexCode", new Character('M' ) ); + int count = s.createQuery( "delete Person" ).executeUpdate(); + assertEquals( 2, count ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.beginTransaction(); + s.createQuery( "delete Person" ).executeUpdate(); + s.getTransaction().commit(); + s.close(); + } + + public void testFilteredJoinedSubclassHqlDeleteNonLeaf() { + Session s = openSession(); + s.beginTransaction(); + s.save( new Employee( "John", 'M', "john", new Date() ) ); + s.save( new Employee( "Jane", 'F', "jane", new Date() ) ); + s.save( new Customer( "Charlie", 'M', "charlie", "Acme" ) ); + s.save( new Customer( "Wanda", 'F', "wanda", "ABC" ) ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.beginTransaction(); + s.enableFilter( "sex" ).setParameter( "sexCode", new Character('M' ) ); + int count = s.createQuery( "delete User" ).executeUpdate(); + assertEquals( 2, count ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.beginTransaction(); + s.createQuery( "delete Person" ).executeUpdate(); + s.getTransaction().commit(); + s.close(); + } + + public void testFilteredJoinedSubclassHqlDeleteLeaf() { + Session s = openSession(); + s.beginTransaction(); + s.save( new Employee( "John", 'M', "john", new Date() ) ); + s.save( new Employee( "Jane", 'F', "jane", new Date() ) ); + s.save( new Customer( "Charlie", 'M', "charlie", "Acme" ) ); + s.save( new Customer( "Wanda", 'F', "wanda", "ABC" ) ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.beginTransaction(); + s.enableFilter( "sex" ).setParameter( "sexCode", new Character('M' ) ); + int count = s.createQuery( "delete Employee" ).executeUpdate(); + assertEquals( 1, count ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.beginTransaction(); + s.createQuery( "delete Person" ).executeUpdate(); + s.getTransaction().commit(); + s.close(); + } + + public void testFilteredJoinedSubclassHqlUpdateRoot() { + Session s = openSession(); + s.beginTransaction(); + s.save( new Employee( "John", 'M', "john", new Date() ) ); + s.save( new Employee( "Jane", 'F', "jane", new Date() ) ); + s.save( new Customer( "Charlie", 'M', "charlie", "Acme" ) ); + s.save( new Customer( "Wanda", 'F', "wanda", "ABC" ) ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.beginTransaction(); + s.enableFilter( "sex" ).setParameter( "sexCode", new Character('M' ) ); + int count = s.createQuery( "update Person p set p.name = ''" ).executeUpdate(); + assertEquals( 2, count ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.beginTransaction(); + s.createQuery( "delete Person" ).executeUpdate(); + s.getTransaction().commit(); + s.close(); + } + + public void testFilteredJoinedSubclassHqlUpdateNonLeaf() { + Session s = openSession(); + s.beginTransaction(); + s.save( new Employee( "John", 'M', "john", new Date() ) ); + s.save( new Employee( "Jane", 'F', "jane", new Date() ) ); + s.save( new Customer( "Charlie", 'M', "charlie", "Acme" ) ); + s.save( new Customer( "Wanda", 'F', "wanda", "ABC" ) ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.beginTransaction(); + s.enableFilter( "sex" ).setParameter( "sexCode", new Character('M' ) ); + int count = s.createQuery( "update User u set u.username = :un where u.name = :n" ) + .setString( "un", "charlie" ) + .setString( "n", "Wanda" ) + .executeUpdate(); + assertEquals( 0, count ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.beginTransaction(); + s.createQuery( "delete Person" ).executeUpdate(); + s.getTransaction().commit(); + s.close(); + } + + public void testFilteredJoinedSubclassHqlUpdateLeaf() { + Session s = openSession(); + s.beginTransaction(); + s.save( new Employee( "John", 'M', "john", new Date() ) ); + s.save( new Employee( "Jane", 'F', "jane", new Date() ) ); + s.save( new Customer( "Charlie", 'M', "charlie", "Acme" ) ); + s.save( new Customer( "Wanda", 'F', "wanda", "ABC" ) ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.beginTransaction(); + s.enableFilter( "sex" ).setParameter( "sexCode", new Character('M' ) ); + int count = s.createQuery( "update Customer c set c.company = 'XYZ'" ).executeUpdate(); + assertEquals( 1, count ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.beginTransaction(); + s.createQuery( "delete Person" ).executeUpdate(); + s.getTransaction().commit(); + s.close(); + } +} diff --git a/test/org/hibernate/test/filter/hql/Person.java b/test/org/hibernate/test/filter/hql/Person.java new file mode 100644 index 0000000000..76b8d4fa68 --- /dev/null +++ b/test/org/hibernate/test/filter/hql/Person.java @@ -0,0 +1,71 @@ +/* + * 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.test.filter.hql; + +/** + * Base of inheritence hierarchy + * + * @author Steve Ebersole + */ +public class Person { + private Long id; + private String name; + private char sex; + + /** + * Used by persistence + */ + protected Person() { + } + + public Person(String name, char sex) { + this.name = name; + this.sex = sex; + } + + public Long getId() { + return id; + } + + private void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public char getSex() { + return sex; + } + + public void setSex(char sex) { + this.sex = sex; + } +} diff --git a/test/org/hibernate/test/filter/hql/User.java b/test/org/hibernate/test/filter/hql/User.java new file mode 100644 index 0000000000..e102cf2a99 --- /dev/null +++ b/test/org/hibernate/test/filter/hql/User.java @@ -0,0 +1,51 @@ +/* + * 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.test.filter.hql; + +/** + * Non-leaf subclass + * + * @author Steve Ebersole + */ +public class User extends Person { + private String username; + + protected User() { + super(); + } + + public User(String name, char sex, String username) { + super( name, sex ); + this.username = username; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } +} diff --git a/test/org/hibernate/test/filter/hql/filter-defs.hbm.xml b/test/org/hibernate/test/filter/hql/filter-defs.hbm.xml new file mode 100644 index 0000000000..3d960c79e8 --- /dev/null +++ b/test/org/hibernate/test/filter/hql/filter-defs.hbm.xml @@ -0,0 +1,35 @@ + + + + + + + + + +