The question is: how do I make GORM generate left joins instead of inner joins in this particular example?
Testbed:
Given classes A, B and C:
class A{ B someObject } class B{ C importantObject } class C{ boolean interestingFlag }
I want to list all the elements of A class that:
What I tried so far:
This approach produces correct list of A where B.C is null (conditional 2 commented out) OR correct list of A where B.C.interestingFlag = false (no matter if conditional 1 is commented out or not). When both conditionals are uncommented it returns only a list of elements where A.B.C.interestingFlag = false (A.B.C = null conditional is ignored)
// approach 1 (conditional 1 is ignored) def result = A.withCriteria{ someObject{ or{ isNull('importantObject') // conditional 1, works well when conditional 2 is commented out importantObject{ eq('interestingFlag', false) // conditional 2, works well alone, discards conditional 1 when both of them are uncommented } } } }
Edit: As requested in comment I'm pasting a hibernate generated sql:
Hibernate: select this_.id as id1_2_, this_.version as version1_2_, this_.some_object_id as some3_1_2_, someobject1_.id as id2_0_, someobject1_.version as version2_0_, someobject1_.important_object_id as important3_2_0_, importanto2_.id as id0_1_, importanto2_.version as version0_1_, importanto2_.interesting_flag as interest3_0_1_ from a this_ inner join b someobject1_ on this_.some_object_id=someobject1_.id inner join c importanto2_ on someobject1_.important_object_id=importanto2_.id where ((someobject1_.important_object_id is null or (importanto2_.interesting_flag=?)))
When I copy and paste it in the pgAdmin query tool directly with a few things changed (inner joins changed to left joins, and provided the interestingFlag = "false" parameter) everything works as I wanted (I get both A.B.C = null and A.B.C.importantFlag = false objects)
Hibernate: select this_.id as id1_2_, this_.version as version1_2_, this_.some_object_id as some3_1_2_, someobject1_.id as id2_0_, someobject1_.version as version2_0_, someobject1_.important_object_id as important3_2_0_, importanto2_.id as id0_1_, importanto2_.version as version0_1_, importanto2_.interesting_flag as interest3_0_1_ from a this_ left join b someobject1_ on this_.some_object_id=someobject1_.id left join c importanto2_ on someobject1_.important_object_id=importanto2_.id where ((someobject1_.important_object_id is null or (importanto2_.interesting_flag=false)))
You'll use INNER JOIN when you want to return only records having pair on both sides, and you'll use LEFT JOIN when you need all records from the “left” table, no matter if they have pair in the “right” table or not.
However, if you change the matching key in the join query from Name to ID and if there are a large number of rows in the table, then you will find that the inner join will be faster than the left outer join.
The reason why LEFT JOIN and INNER JOIN results are the same is because all the records of table branch has at least one match on table user_mast . The main difference between INNER JOIN and LEFT JOIN is that LEFT JOIN still displays the records on the the LEFT side even if they have no match on the RIGHT side table.
INNER is the default; LEFT , RIGHT , and FULL imply an outer join."
Tested and working solution:
def result = A.withCriteria{ createAlias('someObject', 'so', CriteriaSpecification.LEFT_JOIN) createAlias('so.importantObject', 'imp', CriteriaSpecification.LEFT_JOIN) or { isNull('so.importantObject') eq('imp.interestingFlag', false) } }
Solution update as suggested in comment:
def result = A.withCriteria{ createAlias('someObject', 'so', JoinType.LEFT_OUTER_JOIN) createAlias('so.importantObject', 'imp', JoinType.LEFT_OUTER_JOIN) or { isNull('so.importantObject') eq('imp.interestingFlag', false) } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With