Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

criteria uses "inner join" instead "left join" approach by default making my query work not the way I planned

Tags:

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:

  • their B.C object is null OR
  • their B.C object interestingFlag value is false

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))) 
like image 225
Andrzej Bobak Avatar asked Jun 13 '13 09:06

Andrzej Bobak


People also ask

How do you use inner join instead of left join?

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.

Is inner join more efficient than left join?

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.

Can inner join and left join giving same results?

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.

IS LEFT join inner or outer by default?

INNER is the default; LEFT , RIGHT , and FULL imply an outer join."


1 Answers

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)         }       } 
like image 116
Andrzej Bobak Avatar answered Oct 24 '22 07:10

Andrzej Bobak