[hibernate-commits] Hibernate SVN: r18012 - in core/trunk: core/src/main/java/org/hibernate/impl and 5 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Fri Nov 20 00:20:29 EST 2009


Author: steve.ebersole at jboss.com
Date: 2009-11-20 00:20:28 -0500 (Fri, 20 Nov 2009)
New Revision: 18012

Modified:
   core/trunk/core/src/main/java/org/hibernate/Criteria.java
   core/trunk/core/src/main/java/org/hibernate/impl/CriteriaImpl.java
   core/trunk/core/src/main/java/org/hibernate/loader/AbstractEntityJoinWalker.java
   core/trunk/core/src/main/java/org/hibernate/loader/JoinWalker.java
   core/trunk/core/src/main/java/org/hibernate/loader/OuterJoinableAssociation.java
   core/trunk/core/src/main/java/org/hibernate/loader/collection/BasicCollectionJoinWalker.java
   core/trunk/core/src/main/java/org/hibernate/loader/collection/OneToManyJoinWalker.java
   core/trunk/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java
   core/trunk/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java
   core/trunk/documentation/manual/src/main/docbook/en-US/content/query_criteria.xml
   core/trunk/testsuite/src/test/java/org/hibernate/test/criteria/CriteriaQueryTest.java
Log:
HHH-2308 - Adding predicates to the join condition using Criteria Query


Modified: core/trunk/core/src/main/java/org/hibernate/Criteria.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/Criteria.java	2009-11-19 19:45:41 UTC (rev 18011)
+++ core/trunk/core/src/main/java/org/hibernate/Criteria.java	2009-11-20 05:20:28 UTC (rev 18012)
@@ -135,7 +135,10 @@
 	 *
 	 * @param associationPath a dot seperated property path
 	 * @param mode The fetch mode for the referenced association
+	 *
 	 * @return this (for method chaining)
+	 *
+	 * @throws HibernateException Indicates a problem applying the given fetch mode
 	 */
 	public Criteria setFetchMode(String associationPath, FetchMode mode) throws HibernateException;
 
@@ -143,6 +146,7 @@
 	 * Set the lock mode of the current entity
 	 *
 	 * @param lockMode The lock mode to be applied
+	 *
 	 * @return this (for method chaining)
 	 */
 	public Criteria setLockMode(LockMode lockMode);
@@ -151,8 +155,9 @@
 	 * Set the lock mode of the aliased entity
 	 *
 	 * @param alias The previously assigned alias representing the entity to
-	 * which the given lock mode should apply.
+	 *			which the given lock mode should apply.
 	 * @param lockMode The lock mode to be applied
+	 *
 	 * @return this (for method chaining)
 	 */
 	public Criteria setLockMode(String alias, LockMode lockMode);
@@ -165,7 +170,10 @@
 	 *
 	 * @param associationPath A dot-seperated property path
 	 * @param alias The alias to assign to the joined association (for later reference).
+	 *
 	 * @return this (for method chaining)
+	 *
+	 * @throws HibernateException Indicates a problem creating the sub criteria
 	 */
 	public Criteria createAlias(String associationPath, String alias) throws HibernateException;
 
@@ -179,18 +187,42 @@
 	 * @param associationPath A dot-seperated property path
 	 * @param alias The alias to assign to the joined association (for later reference).
 	 * @param joinType The type of join to use.
+	 *
 	 * @return this (for method chaining)
+	 *
+	 * @throws HibernateException Indicates a problem creating the sub criteria
 	 */
 	public Criteria createAlias(String associationPath, String alias, int joinType) throws HibernateException;
 
 	/**
+	 * Join an association using the specified join-type, assigning an alias
+	 * to the joined association.
+	 * <p/>
+	 * The joinType is expected to be one of {@link #INNER_JOIN} (the default),
+	 * {@link #FULL_JOIN}, or {@link #LEFT_JOIN}.
+	 *
+	 * @param associationPath A dot-seperated property path
+	 * @param alias The alias to assign to the joined association (for later reference).
+	 * @param joinType The type of join to use.
+	 * @param withClause The criteria to be added to the join condition (<tt>ON</tt> clause)
+	 *
+	 * @return this (for method chaining)
+	 *
+	 * @throws HibernateException Indicates a problem creating the sub criteria
+	 */
+	public Criteria createAlias(String associationPath, String alias, int joinType, Criterion withClause) throws HibernateException;
+	
+	/**
 	 * Create a new <tt>Criteria</tt>, "rooted" at the associated entity.
 	 * <p/>
 	 * Functionally equivalent to {@link #createCriteria(String, int)} using
 	 * {@link #INNER_JOIN} for the joinType.
 	 *
 	 * @param associationPath A dot-seperated property path
+	 *
 	 * @return the created "sub criteria"
+	 *
+	 * @throws HibernateException Indicates a problem creating the sub criteria
 	 */
 	public Criteria createCriteria(String associationPath) throws HibernateException;
 
@@ -200,7 +232,10 @@
 	 *
 	 * @param associationPath A dot-seperated property path
 	 * @param joinType The type of join to use.
+	 *
 	 * @return the created "sub criteria"
+	 *
+	 * @throws HibernateException Indicates a problem creating the sub criteria
 	 */
 	public Criteria createCriteria(String associationPath, int joinType) throws HibernateException;
 
@@ -213,7 +248,10 @@
 	 *
 	 * @param associationPath A dot-seperated property path
 	 * @param alias The alias to assign to the joined association (for later reference).
+	 *
 	 * @return the created "sub criteria"
+	 *
+	 * @throws HibernateException Indicates a problem creating the sub criteria
 	 */
 	public Criteria createCriteria(String associationPath, String alias) throws HibernateException;
 
@@ -224,11 +262,29 @@
 	 * @param associationPath A dot-seperated property path
 	 * @param alias The alias to assign to the joined association (for later reference).
 	 * @param joinType The type of join to use.
+	 *
 	 * @return the created "sub criteria"
+	 *
+	 * @throws HibernateException Indicates a problem creating the sub criteria
 	 */
 	public Criteria createCriteria(String associationPath, String alias, int joinType) throws HibernateException;
 
 	/**
+	 * Create a new <tt>Criteria</tt>, "rooted" at the associated entity,
+	 * assigning the given alias and using the specified join type.
+	 *
+	 * @param associationPath A dot-seperated property path
+	 * @param alias The alias to assign to the joined association (for later reference).
+	 * @param joinType The type of join to use.
+	 * @param withClause The criteria to be added to the join condition (<tt>ON</tt> clause)
+	 *
+	 * @return the created "sub criteria"
+	 * 
+	 * @throws HibernateException Indicates a problem creating the sub criteria
+	 */
+	public Criteria createCriteria(String associationPath, String alias, int joinType, Criterion withClause) throws HibernateException;
+
+	/**
 	 * Set a strategy for handling the query results. This determines the
 	 * "shape" of the query result.
 	 *
@@ -327,6 +383,9 @@
 	 * Get the results.
 	 *
 	 * @return The list of matched query results.
+	 *
+	 * @throws HibernateException Indicates a problem either translating the criteria to SQL,
+	 * exeucting the SQL or processing the SQL results.
 	 */
 	public List list() throws HibernateException;
 	
@@ -335,6 +394,9 @@
 	 *
 	 * @return The {@link ScrollableResults} representing the matched
 	 * query results.
+	 *
+	 * @throws HibernateException Indicates a problem either translating the criteria to SQL,
+	 * exeucting the SQL or processing the SQL results.
 	 */
 	public ScrollableResults scroll() throws HibernateException;
 
@@ -344,8 +406,12 @@
 	 *
 	 * @param scrollMode Indicates the type of underlying database cursor to
 	 * request.
+	 *
 	 * @return The {@link ScrollableResults} representing the matched
 	 * query results.
+	 *
+	 * @throws HibernateException Indicates a problem either translating the criteria to SQL,
+	 * exeucting the SQL or processing the SQL results.
 	 */
 	public ScrollableResults scroll(ScrollMode scrollMode) throws HibernateException;
 
@@ -358,4 +424,4 @@
 	 */
 	public Object uniqueResult() throws HibernateException;
 	
-}
\ No newline at end of file
+}

Modified: core/trunk/core/src/main/java/org/hibernate/impl/CriteriaImpl.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/impl/CriteriaImpl.java	2009-11-19 19:45:41 UTC (rev 18011)
+++ core/trunk/core/src/main/java/org/hibernate/impl/CriteriaImpl.java	2009-11-20 05:20:28 UTC (rev 18012)
@@ -80,7 +80,7 @@
 	private CacheMode cacheMode;
 	private FlushMode sessionFlushMode;
 	private CacheMode sessionCacheMode;
-	
+
 	private ResultTransformer resultTransformer = Criteria.ROOT_ENTITY;
 
 
@@ -202,6 +202,11 @@
 		return this;
 	}
 
+	public Criteria createAlias(String associationPath, String alias, int joinType, Criterion withClause) {
+		new Subcriteria( this, associationPath, alias, joinType, withClause );
+		return this;
+	}
+
 	public Criteria createCriteria(String associationPath) {
 		return createCriteria( associationPath, INNER_JOIN );
 	}
@@ -218,6 +223,10 @@
 		return new Subcriteria( this, associationPath, alias, joinType );
 	}
 
+	public Criteria createCriteria(String associationPath, String alias, int joinType, Criterion withClause) {
+		return new Subcriteria( this, associationPath, alias, joinType, withClause );
+	}
+
 	public ResultTransformer getResultTransformer() {
 		return resultTransformer;
 	}
@@ -309,7 +318,7 @@
 			after();
 		}
 	}
-	
+
 	public ScrollableResults scroll() {
 		return scroll( ScrollMode.SCROLL_INSENSITIVE );
 	}
@@ -338,7 +347,7 @@
 			getSession().setCacheMode( cacheMode );
 		}
 	}
-	
+
 	protected void after() {
 		if ( sessionFlushMode != null ) {
 			getSession().setFlushMode( sessionFlushMode );
@@ -349,7 +358,7 @@
 			sessionCacheMode = null;
 		}
 	}
-	
+
 	public boolean isLookupByNaturalKey() {
 		if ( projection != null ) {
 			return false;
@@ -374,27 +383,32 @@
 		private Criteria parent;
 		private LockMode lockMode;
 		private int joinType;
+		private Criterion withClause;
 
-
 		// Constructors ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-		private Subcriteria(Criteria parent, String path, String alias, int joinType) {
+		private Subcriteria(Criteria parent, String path, String alias, int joinType, Criterion withClause) {
 			this.alias = alias;
 			this.path = path;
 			this.parent = parent;
 			this.joinType = joinType;
-			CriteriaImpl.this.subcriteriaList.add(this);
+			this.withClause = withClause;
+			CriteriaImpl.this.subcriteriaList.add( this );
 		}
 
+		private Subcriteria(Criteria parent, String path, String alias, int joinType) {
+			this( parent, path, alias, joinType, null );
+		}
+
 		private Subcriteria(Criteria parent, String path, int joinType) {
 			this( parent, path, null, joinType );
 		}
 
 		public String toString() {
-			return "Subcriteria(" +
-				path + ":" +
-				(alias==null ? "" : alias) +
-				')';
+			return "Subcriteria("
+					+ path + ":"
+					+ (alias==null ? "" : alias)
+					+ ')';
 		}
 
 
@@ -429,7 +443,11 @@
 			return joinType;
 		}
 
+		public Criterion getWithClause() {
+			return this.withClause;
+		}
 
+
 		// Criteria impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 		public Criteria add(Criterion expression) {
@@ -451,6 +469,11 @@
 			return this;
 		}
 
+		public Criteria createAlias(String associationPath, String alias, int joinType, Criterion withClause) throws HibernateException {
+			new Subcriteria( this, associationPath, alias, joinType, withClause );
+			return this;
+		}
+
 		public Criteria createCriteria(String associationPath) {
 			return createCriteria( associationPath, INNER_JOIN );
 		}
@@ -467,6 +490,10 @@
 			return new Subcriteria( Subcriteria.this, associationPath, alias, joinType );
 		}
 
+		public Criteria createCriteria(String associationPath, String alias, int joinType, Criterion withClause) throws HibernateException {
+			return new Subcriteria( this, associationPath, alias, joinType, withClause );
+		}
+
 		public Criteria setCacheable(boolean cacheable) {
 			CriteriaImpl.this.setCacheable(cacheable);
 			return this;
@@ -493,8 +520,7 @@
 			return CriteriaImpl.this.uniqueResult();
 		}
 
-		public Criteria setFetchMode(String associationPath, FetchMode mode)
-			throws HibernateException {
+		public Criteria setFetchMode(String associationPath, FetchMode mode) {
 			CriteriaImpl.this.setFetchMode( StringHelper.qualify(path, associationPath), mode);
 			return this;
 		}

Modified: core/trunk/core/src/main/java/org/hibernate/loader/AbstractEntityJoinWalker.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/AbstractEntityJoinWalker.java	2009-11-19 19:45:41 UTC (rev 18011)
+++ core/trunk/core/src/main/java/org/hibernate/loader/AbstractEntityJoinWalker.java	2009-11-20 05:20:28 UTC (rev 18012)
@@ -85,6 +85,7 @@
 						null,
 						alias,
 						JoinFragment.LEFT_OUTER_JOIN,
+						null,
 						getFactory(),
 						CollectionHelper.EMPTY_MAP
 				)

Modified: core/trunk/core/src/main/java/org/hibernate/loader/JoinWalker.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/JoinWalker.java	2009-11-19 19:45:41 UTC (rev 18011)
+++ core/trunk/core/src/main/java/org/hibernate/loader/JoinWalker.java	2009-11-20 05:20:28 UTC (rev 18012)
@@ -207,6 +207,10 @@
 		}
 	}
 
+	protected String getWithClause(String path)	{
+		return "";
+	}
+	
 	/**
 	 * Add on association (one-to-one, many-to-one, or a collection) to a list 
 	 * of associations to be fetched by outerjoin 
@@ -235,6 +239,7 @@
 				aliasedLhsColumns, 
 				subalias, 
 				joinType, 
+				getWithClause(path),
 				getFactory(),
 				loadQueryInfluencers.getEnabledFilters()
 		);

Modified: core/trunk/core/src/main/java/org/hibernate/loader/OuterJoinableAssociation.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/OuterJoinableAssociation.java	2009-11-19 19:45:41 UTC (rev 18011)
+++ core/trunk/core/src/main/java/org/hibernate/loader/OuterJoinableAssociation.java	2009-11-20 05:20:28 UTC (rev 18012)
@@ -59,6 +59,7 @@
 			String[] lhsColumns,
 			String rhsAlias,
 			int joinType,
+			String withClause,
 			SessionFactoryImplementor factory,
 			Map enabledFilters) throws MappingException {
 		this.joinableType = joinableType;
@@ -68,7 +69,8 @@
 		this.joinType = joinType;
 		this.joinable = joinableType.getAssociatedJoinable(factory);
 		this.rhsColumns = JoinHelper.getRHSColumnNames(joinableType, factory);
-		this.on = joinableType.getOnCondition(rhsAlias, factory, enabledFilters);
+		this.on = joinableType.getOnCondition(rhsAlias, factory, enabledFilters)
+			+ ( withClause == null || withClause.trim().length() == 0 ? "" : " and ( " + withClause + " )" );
 		this.enabledFilters = enabledFilters; // needed later for many-to-many/filter application
 	}
 
@@ -188,4 +190,4 @@
 			joinable.whereJoinFragment(rhsAlias, false, true)
 		);
 	}
-}
\ No newline at end of file
+}

Modified: core/trunk/core/src/main/java/org/hibernate/loader/collection/BasicCollectionJoinWalker.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/collection/BasicCollectionJoinWalker.java	2009-11-19 19:45:41 UTC (rev 18011)
+++ core/trunk/core/src/main/java/org/hibernate/loader/collection/BasicCollectionJoinWalker.java	2009-11-20 05:20:28 UTC (rev 18012)
@@ -80,6 +80,7 @@
 						null,
 						alias,
 						JoinFragment.LEFT_OUTER_JOIN,
+						null,
 						getFactory(),
 						CollectionHelper.EMPTY_MAP
 				)

Modified: core/trunk/core/src/main/java/org/hibernate/loader/collection/OneToManyJoinWalker.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/collection/OneToManyJoinWalker.java	2009-11-19 19:45:41 UTC (rev 18011)
+++ core/trunk/core/src/main/java/org/hibernate/loader/collection/OneToManyJoinWalker.java	2009-11-20 05:20:28 UTC (rev 18012)
@@ -85,6 +85,7 @@
 				null, 
 				alias, 
 				JoinFragment.LEFT_OUTER_JOIN, 
+				null,
 				getFactory(), 
 				CollectionHelper.EMPTY_MAP 
 			) );

Modified: core/trunk/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java	2009-11-19 19:45:41 UTC (rev 18011)
+++ core/trunk/core/src/main/java/org/hibernate/loader/criteria/CriteriaJoinWalker.java	2009-11-20 05:20:28 UTC (rev 18012)
@@ -210,4 +210,8 @@
 		return "criteria query";
 	}
 
+	protected String getWithClause(String path) {
+		return translator.getWithClause(path); 
+	}
+	
 }

Modified: core/trunk/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java	2009-11-19 19:45:41 UTC (rev 18011)
+++ core/trunk/core/src/main/java/org/hibernate/loader/criteria/CriteriaQueryTranslator.java	2009-11-20 05:20:28 UTC (rev 18012)
@@ -43,6 +43,7 @@
 import org.hibernate.QueryException;
 import org.hibernate.hql.ast.util.SessionFactoryHelper;
 import org.hibernate.criterion.CriteriaQuery;
+import org.hibernate.criterion.Criterion;
 import org.hibernate.criterion.Projection;
 import org.hibernate.engine.QueryParameters;
 import org.hibernate.engine.RowSelection;
@@ -77,7 +78,8 @@
 	private final Map aliasCriteriaMap = new HashMap();
 	private final Map associationPathCriteriaMap = new LinkedHashMap();
 	private final Map associationPathJoinTypesMap = new LinkedHashMap();
-
+	private final Map withClauseMap = new HashMap();
+	
 	private final SessionFactoryImplementor sessionFactory;
 
 	public CriteriaQueryTranslator(
@@ -169,6 +171,10 @@
 				// TODO : not so sure this is needed...
 				throw new QueryException( "duplicate association path: " + wholeAssociationPath );
 			}
+			if ( crit.getWithClause() != null )
+			{
+				this.withClauseMap.put(wholeAssociationPath, crit.getWithClause());
+			}
 		}
 	}
 
@@ -266,20 +272,6 @@
 	}
 
 	public QueryParameters getQueryParameters() {
-		List values = new ArrayList();
-		List types = new ArrayList();
-		Iterator iter = rootCriteria.iterateExpressionEntries();
-		while ( iter.hasNext() ) {
-			CriteriaImpl.CriterionEntry ce = ( CriteriaImpl.CriterionEntry ) iter.next();
-			TypedValue[] tv = ce.getCriterion().getTypedValues( ce.getCriteria(), this );
-			for ( int i = 0; i < tv.length; i++ ) {
-				values.add( tv[i].getValue() );
-				types.add( tv[i].getType() );
-			}
-		}
-		Object[] valueArray = values.toArray();
-		Type[] typeArray = ArrayHelper.toTypeArray( types );
-
 		RowSelection selection = new RowSelection();
 		selection.setFirstRow( rootCriteria.getFirstResult() );
 		selection.setMaxRows( rootCriteria.getMaxResults() );
@@ -287,12 +279,14 @@
 		selection.setFetchSize( rootCriteria.getFetchSize() );
 
 		Map lockModes = new HashMap();
-		iter = rootCriteria.getLockModes().entrySet().iterator();
+		Iterator iter = rootCriteria.getLockModes().entrySet().iterator();
 		while ( iter.hasNext() ) {
 			Map.Entry me = ( Map.Entry ) iter.next();
 			final Criteria subcriteria = getAliasedCriteria( ( String ) me.getKey() );
 			lockModes.put( getSQLAlias( subcriteria ), me.getValue() );
 		}
+		List values = new ArrayList();
+		List types = new ArrayList();
 		iter = rootCriteria.iterateSubcriteria();
 		while ( iter.hasNext() ) {
 			CriteriaImpl.Subcriteria subcriteria = ( CriteriaImpl.Subcriteria ) iter.next();
@@ -300,8 +294,31 @@
 			if ( lm != null ) {
 				lockModes.put( getSQLAlias( subcriteria ), lm );
 			}
+			if ( subcriteria.getWithClause() != null )
+			{
+				TypedValue[] tv = subcriteria.getWithClause().getTypedValues( subcriteria, this );
+				for ( int i = 0; i < tv.length; i++ ) {
+					values.add( tv[i].getValue() );
+					types.add( tv[i].getType() );
+				}
+			}
 		}
 
+		// Type and value gathering for the WHERE clause needs to come AFTER lock mode gathering,
+		// because the lock mode gathering loop now contains join clauses which can contain
+		// parameter bindings (as in the HQL WITH clause).
+		iter = rootCriteria.iterateExpressionEntries();
+		while ( iter.hasNext() ) {
+			CriteriaImpl.CriterionEntry ce = ( CriteriaImpl.CriterionEntry ) iter.next();
+			TypedValue[] tv = ce.getCriterion().getTypedValues( ce.getCriteria(), this );
+			for ( int i = 0; i < tv.length; i++ ) {
+				values.add( tv[i].getValue() );
+				types.add( tv[i].getType() );
+			}
+		}
+
+		Object[] valueArray = values.toArray();
+		Type[] typeArray = ArrayHelper.toTypeArray( types );
 		return new QueryParameters(
 				typeArray,
 		        valueArray,
@@ -576,4 +593,10 @@
 		return propertyName;
 	}
 
+	public String getWithClause(String path)
+	{
+		final Criterion crit = (Criterion)this.withClauseMap.get(path);
+		return crit == null ? null : crit.toSqlString(getCriteria(path), this);
+	}
+	
 }

Modified: core/trunk/documentation/manual/src/main/docbook/en-US/content/query_criteria.xml
===================================================================
--- core/trunk/documentation/manual/src/main/docbook/en-US/content/query_criteria.xml	2009-11-19 19:45:41 UTC (rev 18011)
+++ core/trunk/documentation/manual/src/main/docbook/en-US/content/query_criteria.xml	2009-11-20 05:20:28 UTC (rev 18012)
@@ -196,6 +196,34 @@
     Cat kitten = (Cat) map.get("kt");
 }]]></programlisting>
 
+	<para>
+		Additionally you may manipulate the result set using a left outer join:
+	</para>
+	<programlisting><![CDATA[
+		List cats = session.createCriteria( Cat.class )
+                       .createAlias("mate", "mt", Criteria.LEFT_JOIN, Restrictions.like("mt.name", "good%") )
+                       .addOrder(Order.asc("mt.age"))
+                       .list();
+	
+	]]></programlisting>
+
+	<para>
+		This will return all of the <literal>Cat</literal>s with a mate whose name starts with "good"
+		ordered by their mate's age, and all cats who do not have a mate.  
+		 This is useful when there is a need to order or limit in the database
+		 prior to returning complex/large result sets, and removes many instances where
+		 multiple queries would have to be performed and the results unioned 
+		 by java in memory.  
+	</para>
+	<para>
+		Without this feature, first all of the cats without a mate would need to be loaded in one query. 
+	</para>
+	<para>
+		A second query would need to retreive the cats with mates who's name started with "good" sorted by the mates age.
+	</para>
+	<para>
+		Thirdly, in memory; the lists would need to be joined manually.
+	</para>
     </sect1>
     
     <sect1 id="querycriteria-dynamicfetching" revision="1">

Modified: core/trunk/testsuite/src/test/java/org/hibernate/test/criteria/CriteriaQueryTest.java
===================================================================
--- core/trunk/testsuite/src/test/java/org/hibernate/test/criteria/CriteriaQueryTest.java	2009-11-19 19:45:41 UTC (rev 18011)
+++ core/trunk/testsuite/src/test/java/org/hibernate/test/criteria/CriteriaQueryTest.java	2009-11-20 05:20:28 UTC (rev 18012)
@@ -794,5 +794,99 @@
 		t.commit();
 		session.close();
 	}
+	
+	public void testAliasJoinCriterion() {
+		Session session = openSession();
+		Transaction t = session.beginTransaction();
+
+		Course courseA = new Course();
+		courseA.setCourseCode("HIB-A");
+		courseA.setDescription("Hibernate Training A");
+		session.persist(courseA);
+
+		Course courseB = new Course();
+		courseB.setCourseCode("HIB-B");
+		courseB.setDescription("Hibernate Training B");
+		session.persist(courseB);
+
+		Student gavin = new Student();
+		gavin.setName("Gavin King");
+		gavin.setStudentNumber(232);
+		gavin.setPreferredCourse(courseA);
+		session.persist(gavin);
+
+		Student leonardo = new Student();
+		leonardo.setName("Leonardo Quijano");
+		leonardo.setStudentNumber(233);
+		leonardo.setPreferredCourse(courseB);
+		session.persist(leonardo);
+
+		Student johnDoe = new Student();
+		johnDoe.setName("John Doe");
+		johnDoe.setStudentNumber(235);
+		johnDoe.setPreferredCourse(null);
+		session.persist(johnDoe);
+
+		// test == on one value exists
+		List result = session.createCriteria( Student.class )
+			.createAlias( "preferredCourse", "pc", Criteria.LEFT_JOIN, Restrictions.eq("pc.courseCode", "HIB-A") )
+			.setProjection( Property.forName("pc.courseCode") )
+			.addOrder(Order.asc("pc.courseCode"))
+			.list();
+		
+		assertEquals( 3, result.size() );
+		
+		// can't be sure of NULL comparison ordering aside from they should
+		// either come first or last
+		if ( result.get( 0 ) == null ) {
+			assertNull(result.get(1));
+			assertEquals( "HIB-A", result.get(2) );
+		}
+		else {
+			assertNull( result.get(2) );
+			assertNull( result.get(1) );
+			assertEquals( "HIB-A", result.get(0) );
+		}
+		
+		// test == on non existent value
+		result = session.createCriteria( Student.class )
+		.createAlias( "preferredCourse", "pc", Criteria.LEFT_JOIN, Restrictions.eq("pc.courseCode", "HIB-R") )
+		.setProjection( Property.forName("pc.courseCode") )
+		.addOrder(Order.asc("pc.courseCode"))
+		.list();
+	
+		assertEquals( 3, result.size() );
+		assertNull( result.get(2) );
+		assertNull( result.get(1) );
+		assertNull(result.get(0) );
+		
+		// test != on one existing value
+		result = session.createCriteria( Student.class )
+		.createAlias( "preferredCourse", "pc", Criteria.LEFT_JOIN, Restrictions.ne("pc.courseCode", "HIB-A") )
+		.setProjection( Property.forName("pc.courseCode") )
+		.addOrder(Order.asc("pc.courseCode"))
+		.list();
+	
+		assertEquals( 3, result.size() );
+		// can't be sure of NULL comparison ordering aside from they should
+		// either come first or last
+		if ( result.get( 0 ) == null ) {
+			assertNull( result.get(1) );
+			assertEquals( "HIB-B", result.get(2) );
+		}
+		else {
+			assertEquals( "HIB-B", result.get(0) );
+			assertNull( result.get(1) );
+			assertNull( result.get(2) );
+		}
+
+		session.delete(gavin);
+		session.delete(leonardo);
+		session.delete(johnDoe);
+		session.delete(courseA);
+		session.delete(courseB);
+		t.commit();
+		session.close();
+	}
 }
 



More information about the hibernate-commits mailing list