Author: steve.ebersole(a)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();
+ }
}