How to use LEFT JOIN in Hibernate Criteria

January 29th, 2009  | Tags: , ,

Criteria, in my opinion, is the most fantastic mechanism for querying ever created. Hibernate has an excellent API for Criteria and many users use it a lot, including me :) . However by default, Hibernate Criteria uses INNER JOIN internally and depends on the situation, it doesn’t work as expected.

As you probably know, INNER and LEFT JOIN are quite different. Generally speaking, INNER JOIN is used when both sides of the associations exists, as long as LEFT JOIN is used when only the LEFT side of the relationship exists. Let’s look at a real example.

Supposing we have the following diagram:

As you can see above, there is a relationship One-To-Many between Person and Children. As usual, One Person can have none or more children, as well as a Children has only one Father (at least, biological).

Now, supposing we wanna a list of all People. If he/she has children, show up the list of them, otherwise, show up only the Person’s name. To do that, we could create an HQL like below:

Query query = getSession().createQuery("SELECT p FROM Person p LEFT JOIN p.listChildren children");

Note: We’ve used LEFT JOIN, but why? Because if the PERSON has no relationship (no children) the query MUST retrieve the values as well. If the query was like this:

Query query = getSession().createQuery("SELECT p FROM Person p INNER JOIN p.listChildren children");

The Person with no relationship (no children) will not be retrieved by the query. The INNER JOIN looks in both side of the relationship. It’s not good for that requirement.

Building the same query using Criteria

Supposing we’re going to create the same HQL above but using Criteria. The sintaxe syntax could be:

Criteria criteria = getSession().createCriteria(Person.class);
criteria.addAlias("listChildren","children");
criteria.list();

Hibernate will read the code above and transform it in an HQL that uses INNER JOIN. The problem with that approach is that the Person with no Children will not be retrieved. So, how to change the default behavior from Hibernate Criteria Associations?

Using LEFT JOIN in Hibernate Criteria

Fortunatelly, there is a simple way to change the default behavior from Hibernate. Simply use the “CriteriaSpecification.LEFT_JOIN” argument. Hence, the Criteria above would be:

Criteria criteria = getSession().createCriteria(Person.class);
criteria.addAlias("listChildren","children",CriteriaSpecification.LEFT_JOIN);
criteria.list();

Conclusion

Criteria API is a powerful mechanism for querying in Hibernate. It can be used for simple and advanced queries. As you saw in this post, the Criteria API is also flexible. Try to use Criteria when you have dynamic parameters. Do not worry about SQL, HQL or anything else besides Objects :) .

I hope this topic be useful. If you have any question or comment, fell free to leave your message below.

  1. Andrew Spencer
    May 29th, 2009 at 12:30
    #1

    Thanks, you saved me some headaches with this tip.

  2. billson
    February 26th, 2010 at 12:41
    #2

    yeah,finally i got this…
    using criteria to do left join.
    tks man.

  3. March 18th, 2010 at 12:33
    #3

    Thank you, you saved me many hours of work

  4. Pravin
    March 19th, 2010 at 16:01
    #4

    Thanks dude. Cleared concepts of left join. I was struggling with it for a while. Very simple but effective usage of the Parent – Child example

  5. Mauro
    April 1st, 2010 at 14:55
    #5

    Thank you for sharing !!! You saved me many hours !!!

  6. April 16th, 2010 at 18:12
    #6

    Thank you. Neat, simple article and compact. Well written

  7. Sridevi
    June 8th, 2010 at 10:26
    #7

    Hi,
    Could you please tell me , how to use full join with Hibernate+MySQL .
    Thanks in advance.
    Sridevi.

  8. June 8th, 2010 at 13:00
    #8

    Sridevi, to use full join, simply remove the CriteriaSpecification parameter. In the example above, you use:
    criteria.addAlias(“listChildren”,”children”);

  9. June 9th, 2010 at 04:24
    #9

    Thank you. You saved my time.

  10. Claudio
    September 7th, 2010 at 23:37
    #10

    Thank you man! Exaclty what I was looking for :)

  11. October 21st, 2010 at 09:24
    #11

    Thank you man, really nice and short way to learn hibernate criteria specification…great

  12. Naresh
    December 20th, 2010 at 19:09
    #12

    Could you please tell me, how to fetch the person whose child name is ‘childName’ using criteria.

  13. Jeeva
    March 21st, 2011 at 02:54
    #13

    I apply Criteriaspecification left join process-how to check the condition(ex:in sql query(left join tablename a on(a.staus=’A')) that process how to apply multiple table in jpa.
    kindly tell me please

  14. May 13th, 2011 at 10:53
    #14

    For example: If the children have a age, how can I get the average of them?
    And if I need use this average in a clause where, for example, how many children are older than 11 years old. How can it works?

    PS: Logically using Criteria! :D

  15. May 27th, 2011 at 15:27
    #15

    thanks ,clear explanation.

  16. May 27th, 2011 at 15:30
    #16

    Still now i has searched for joins in HQL finnaly your explanation stopped me from searching.thanks

  17. xander
    June 2nd, 2011 at 01:47
    #17

    may i know the annotations of the table?

    i am trying to replicate this scenario on my project. it doesnt seem to work.

    please help.

  18. June 6th, 2011 at 11:43
    #18

    Thanks, very usefull

  19. yoyo
    June 9th, 2011 at 12:45
    #19

    Hi Jair,

    Great article! I see you are a big fan of hibernate criteria. I am in a bit of a predicament where I am joining two entities where one id is ‘integer’ (a) and one is ‘numeric’ (b). This is connexting to a db2 database. They join fine but when I run my queries they do not touch the db indexes. I am looking for a way to join the two entities and cast the numeric id to an integer as part of the join criteria. Do you know how to do this? The sql I want for the join is:

    a.key=cast(b.key as integer)

    This change makes a big difference to performance. An obvious solution is to change the datatypes in the db but unfortunately this is out of my control.

    Any comments/suggestions welcome!

    Cheers,
    S

  20. Deepti
    June 18th, 2011 at 02:20
    #20

    Thanks…Very Easy and Good Example.

  21. Gaston Lopez
    July 7th, 2011 at 12:10
    #21

    Hi, I have problem to apply this.
    I don’t have method addAlias.
    Neither I have field LEFT_JOIN on CriteriaSpecification.

  22. July 8th, 2011 at 09:51
    #22

    Hi Gaston,

    Please, check your Hibernate version. I guess you’re using a different version from this post. Actually, the post has been written using Hibernate 3.

  23. Stephane
    July 25th, 2011 at 12:29
    #23

    sintaxe or syntax :-)

  24. July 25th, 2011 at 13:51
    #24

    Hi Stephane,

    Thanks for correcting me. The mistake has been fixed :)

  25. Pao
    August 20th, 2011 at 06:00
    #25

    How can i add another filter on the right table?
    eg. left join children as child on p.id = child.id and child.gender=male.

  26. August 20th, 2011 at 09:27
    #26

    Hi Pao,

    You can use an ALIAS. Check it out on the documentation and you’ll see how it does work.

  27. Pao
    August 20th, 2011 at 14:42
    #27

    Hi Jair,

    i’ve found createCriteria(path,alias,joinType,withClause)… but this limits me to only one criteria after the join tablename on.. What if i need to have more than one criteria in the join column?

    eg.

    left join children as child on p.id = child.id and child.gender=male and childage > 5 and ……

    Thanks,
    Pao

  28. Sayantan
    September 8th, 2011 at 07:19
    #28

    Hi Jair,

    Seeing the example you have given above, I understand this criteria API provided Hibernate seems to list out all the columns of the PERSON table. But, what if I want to select only specific columns? How can I implement it using Hibernate Criteria?

    Thanks in advance.

  29. September 8th, 2011 at 16:09
    #29

    Hi Sayantan,

    In this case, you must use another artefact called Projection.

  30. Ganesh
    September 16th, 2011 at 07:08
    #30

    Hi Jair,

    Thanks…for Very Good Example. I need your help please help on this.

    I have Two table having GUID column when I am using inner join and try to fetch the valu due to guid column i am not able to get the value its giving error due to its string.
    Problem in –: Add(NHibernate.Criterion.Restrictions.Eq(“CT.ID”, “e.ID”))
    see below my query. and pls tell me how to comapare the ID field of two table.

    var CodeTypeSystem = session.CreateCriteria(typeof(CodeTypeSystem), “e”)
    .CreateAlias(“CodeType”, “CT”, CriteriaSpecification.InnerJoin)
    .Add(NHibernate.Criterion.Restrictions.Eq(“CT.Code”, codeType))
    .Add(NHibernate.Criterion.Restrictions.Eq(“CT.ID”, “e.ID”))
    .List();

    Thanks Advance
    Ganesh

  31. Sayantan
    September 16th, 2011 at 07:35
    #31

    Thanks Jair,

    I believe you must be knowing the usage of Embedded Class which we use to define composite primary keys in an Entity class. However, it seems Hibernate Criteria is not able to read joins if I”m using Embedded objects to refer to an Entity class. Consider the following example:-

    I’ve a three tables A, B & AB, where AB holds relationship between A & B and hence has a composite primary key which comprises of the primary key of A & primary key of B. I represent the relationship in hibernate as below (i’m using annotations):-

    Entity class A.java
    ——————–

    //imports…..

    @Entity
    @Table(name=”A”)
    public class A{

    private long aId;
    ….//other properties

    private List bList; //I’ve deliberately given the name bList since using this I can get data of B.

    @Id
    @Column(name=”A_ID”)
    public long getAId(){
    return aId;
    }
    public void setAId(long aId){
    this.aId = aId;
    }

    @OneToMany(mappedBy=”pk.a”)
    public List getBList(){
    return bList;
    }
    public void setBList(List bList){
    this.bList = bList;
    }

    }

    Entity class B.java
    ——————–

    //imports…..

    @Entity
    @Table(name=”B”)
    public class B{

    private long bId;
    private String name;
    ….//other properties

    private List aList; //I’ve deliberately given the name bList since using this I can get data of A.

    @Id
    @Column(name=”B_ID”)
    public long getBId(){
    return bId;
    }
    public void setId(long bId){
    this.bId = bId;
    }

    @Column(name=”B_NM”)
    public String getName(){
    return name;
    }
    public void setName(String name){
    this.name = name;
    }

    @OneToMany(mappedBy=”pk.b”)
    public List getAList(){
    return aList;
    }
    public void setAList(List aList){
    this.aList = aList;
    }

    }

    Entity class AB.java
    ——————–

    //imports…..

    @Entity
    @Table(name=”AB”)
    public class AB{

    private AB_pk pk;
    ….//other properties

    //Constructor to set PK object
    public AB(A a, B b){
    pk = new AB_pk();
    pk.setA(a);
    pk.setB(b);
    }

    @EmbeddedId
    public AB_pk getPk(){
    return pk;
    }
    public void setPk(AB_pk pk){
    this.pk = pk;
    }

    }

    Embeddable class AB_pk.java
    —————————–

    //imports…..

    @Embeddable
    public class AB{

    private A a;
    private B b;

    public AB_pk(){ } //Empty constructor – required

    @ManyToOne
    @JoinColumn(name=”A_ID”)
    public A getA(){
    return a;
    }
    public void setA(A a){
    this.a = a;
    }

    @ManyToOne
    @JoinColumn(name=”B_ID”)
    public B getB(){
    return b;
    }
    public void setB(B b){
    this.b = b;
    }

    }

    Criteria code block
    ——————–

    Criteria criteria = session.createCriteria(A.class);
    criteria.createCriteria(“bList”, “ab”, CriteriaSpecification.INNER_JOIN);
    criteria.createAlias(“ab.pk”, “pk”);
    criteria.createCriteria(“pk.B”, “b”, CriteriaSpecification.INNER_JOIN);
    ProjectionList projections = Projections.projectionList();
    projections.add(Projections.property(“b.name”), “bName”);
    criteria.setProjection(projections);
    criteria.add(Restrictions.eq(“aId”, Long.valueOf(2733)));
    List list = criteria.list();

    SQL Query formed by Hibernate
    ——————————

    SELECT b.B_NM FROM A a INNER JOIN AB ab WHERE a.A_ID = 2733

    If you notice that it’s able to identify B_NM column through the Embedded ID relationship in Projections but the inner join specified in Criteria wasn’t added by hibernate in the SQL query generated by itself.

    Do you know where the problem is? Am I missing something here?

  32. Ganesh
    September 16th, 2011 at 12:44
    #32

    Hi Jair,
    Thanks…for Very Good Example. I need your help please help on this.
    I have Two table having GUID column when I am using inner join and try to fetch the valu due to guid column i am not able to get the value its giving error due to its string.
    Problem in –: Add(NHibernate.Criterion.Restrictions.Eq(“CT.ID”, “e.ID”))
    see below my query. and pls tell me how to comapare the ID field of two table.
    var CodeTypeSystem = session.CreateCriteria(typeof(CodeTypeSystem), “e”)
    .CreateAlias(“CodeType”, “CT”, CriteriaSpecification.InnerJoin)
    .Add(NHibernate.Criterion.Restrictions.Eq(“CT.Code”, codeType))
    .Add(NHibernate.Criterion.Restrictions.Eq(“CT.ID”, “e.ID”))
    .List();
    Thanks Advance
    Ganesh

  33. Raghu
    December 27th, 2011 at 05:39
    #33

    Hi Jair,
    Thanks for nice example. I have 3 tables and corresponding entities. I need to join 3 tables using criteria. I hope you can solve this problem.

    Thanks,
    Raghu

TOP