Author: steve.ebersole(a)jboss.com
Date: 2006-12-05 14:05:47 -0500 (Tue, 05 Dec 2006)
New Revision: 10930
Modified:
trunk/Hibernate3/src/org/hibernate/hql/ast/tree/DotNode.java
trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java
trunk/Hibernate3/test/org/hibernate/test/hql/QueryTranslatorTestCase.java
Log:
HHH-2257 : implicit joins and shallow queries
Modified: trunk/Hibernate3/src/org/hibernate/hql/ast/tree/DotNode.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/hql/ast/tree/DotNode.java 2006-12-05 19:04:53 UTC
(rev 10929)
+++ trunk/Hibernate3/src/org/hibernate/hql/ast/tree/DotNode.java 2006-12-05 19:05:47 UTC
(rev 10930)
@@ -286,26 +286,34 @@
private void dereferenceEntity(EntityType entityType, boolean implicitJoin, String
classAlias, boolean generateJoin, AST parent) throws SemanticException {
checkForCorrelatedSubquery( "dereferenceEntity" );
- // Only join to the entity table if:
- // 1) we were instructed to generate any needed joins (generateJoins==true)
- // AND
- // 2) EITHER:
- // A) our parent represents a further dereference of this entity to anything
- // other than the entity's id property
- // OR
- // B) this node is in any clause, other than the select clause (unless that
- // select clause is part of a scalar query :/ )
+ // two general cases we check here:
+ // 1) is our parent a DotNode as well? If so, our property reference is
+ // being further de-referenced...
+ // 2) we were asked to generate any needed joins (generateJoins==true) *OR*
+ // the current parser state indicates to use an INNER JOIN for implicit joins
+ //
+ // The "implicit join" portion of the second condition was done to account
for
+ // situations like HHH-2257 to make sure that iterate() and list() return
+ // consistent results. Previously the join would be skipped on "shallow
queries"
+ // (aka, iterate()) for performance reasons, but that can lead to incorrect
+ // results since INNER JOINs do place extra restrications on the returned
+ // results.
DotNode parentAsDotNode = null;
String property = propertyName;
final boolean joinIsNeeded;
if ( isDotNode( parent ) ) {
+ // our parent is another dot node, meaning we are being further dereferenced.
+ // thus we need to generate a join unless the parent refers to the associated
+ // entity's PK (because 'our' table would know the FK).
parentAsDotNode = ( DotNode ) parent;
property = parentAsDotNode.propertyName;
joinIsNeeded = generateJoin && !isReferenceToPrimaryKey(
parentAsDotNode.propertyName, entityType );
}
else {
- joinIsNeeded = generateJoin && ( !getWalker().isInSelect() ||
!getWalker().isShallowQuery() );
+ // otherwise we need to generate the join if we were asked to, or
+ // if the current implicit join type is INNER JOINs
+ joinIsNeeded = generateJoin || getWalker().getImpliedJoinType() ==
JoinFragment.INNER_JOIN;
}
if ( joinIsNeeded ) {
Modified: trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java 2006-12-05
19:04:53 UTC (rev 10929)
+++ trunk/Hibernate3/test/org/hibernate/test/hql/ASTParserLoadingTest.java 2006-12-05
19:05:47 UTC (rev 10930)
@@ -76,6 +76,7 @@
return new String[] {
"hql/Animal.hbm.xml",
"hql/FooBarCopy.hbm.xml",
+ "hql/SimpleEntityWithAssociation.hbm.xml",
"batchfetch/ProductLine.hbm.xml",
"cid/Customer.hbm.xml",
"cid/Order.hbm.xml",
@@ -93,6 +94,94 @@
cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
}
+ public void testImplicitJoinsInDifferentClauses() {
+ // HHH-2257 :
+ // both the classic and ast translators output the same syntactically valid sql
+ // for all of these cases; the issue is that shallow (iterate) and
+ // non-shallow (list/scroll) queries return different results because the
+ // shallow skips the inner join which "weeds out" results from the
non-shallow queries.
+ // The results were initially different depending upon the clause(s) in which the
+ // implicit join occurred
+ Session s = openSession();
+ s.beginTransaction();
+ SimpleEntityWithAssociation owner = new SimpleEntityWithAssociation( "owner"
);
+ SimpleAssociatedEntity e1 = new SimpleAssociatedEntity( "thing one", owner
);
+ SimpleAssociatedEntity e2 = new SimpleAssociatedEntity( "thing two" );
+ s.save( e1 );
+ s.save( e2 );
+ s.save( owner );
+ s.getTransaction().commit();
+ s.close();
+
+ checkCounts( "select e.owner from SimpleAssociatedEntity e", 1,
"implicit-join in select clause" );
+ checkCounts( "select e.id, e.owner from SimpleAssociatedEntity e", 1,
"implicit-join in select clause" );
+ checkCounts( "from SimpleAssociatedEntity e order by e.owner", 1,
"implicit-join in order-by clause" );
+ checkCounts( "select e.owner.id, count(*) from SimpleAssociatedEntity e group by
e.owner", 1, "implicit-join in select and group-by clauses" );
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( e1 );
+ s.delete( e2 );
+ s.delete( owner );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ private void checkCounts(String hql, int expected, String testCondition) {
+ Session s = openSession();
+ s.beginTransaction();
+ int count = determineCount( s.createQuery( hql ).list().iterator() );
+ assertEquals( "list() [" + testCondition + "]", expected, count );
+ count = determineCount( s.createQuery( hql ).iterate() );
+ assertEquals( "iterate() [" + testCondition + "]", expected, count
);
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testImplicitSelectEntityAssociationInShallowQuery() {
+ // HHH-2257 :
+ // both the classic and ast translators output the same syntactically valid sql.
+ // the issue is that shallow and non-shallow queries return different
+ // results because the shallow skips the inner join which "weeds out"
results
+ // from the non-shallow queries...
+ Session s = openSession();
+ s.beginTransaction();
+ SimpleEntityWithAssociation owner = new SimpleEntityWithAssociation( "owner"
);
+ SimpleAssociatedEntity e1 = new SimpleAssociatedEntity( "thing one", owner
);
+ SimpleAssociatedEntity e2 = new SimpleAssociatedEntity( "thing two" );
+ s.save( e1 );
+ s.save( e2 );
+ s.save( owner );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ int count = determineCount( s.createQuery( "select e.id, e.owner from
SimpleAssociatedEntity e" ).list().iterator() );
+ assertEquals( 1, count ); // thing two would be removed from the result due to the
inner join
+ count = determineCount( s.createQuery( "select e.id, e.owner from
SimpleAssociatedEntity e" ).iterate() );
+ assertEquals( 1, count );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( e1 );
+ s.delete( e2 );
+ s.delete( owner );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ private int determineCount(Iterator iterator) {
+ int count = 0;
+ while( iterator.hasNext() ) {
+ count++;
+ iterator.next();
+ }
+ return count;
+ }
+
public void testNestedComponentIsNull() {
// (1) From MapTest originally...
// (2) Was then moved into HQLTest...
Modified: trunk/Hibernate3/test/org/hibernate/test/hql/QueryTranslatorTestCase.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/hql/QueryTranslatorTestCase.java 2006-12-05
19:04:53 UTC (rev 10929)
+++ trunk/Hibernate3/test/org/hibernate/test/hql/QueryTranslatorTestCase.java 2006-12-05
19:05:47 UTC (rev 10930)
@@ -498,6 +498,8 @@
return new String[] {
"hql/Animal.hbm.xml",
"hql/EntityWithCrazyCompositeKey.hbm.xml",
+ "hql/CrazyIdFieldNames.hbm.xml",
+ "hql/SimpleEntityWithAssociation.hbm.xml",
"batchfetch/ProductLine.hbm.xml",
"cid/Customer.hbm.xml",
"cid/Order.hbm.xml",
@@ -528,7 +530,6 @@
"legacy/UpDown.hbm.xml",
"compositeelement/Parent.hbm.xml",
"onetoone/joined/Person.hbm.xml",
- "hql/CrazyIdFieldNames.hbm.xml",
"any/Properties.hbm.xml"
};
}