Search code examples
hibernatehqleager-loading

Hibernate HQL eager load collection in embedded object


I have a class that has embedded object

public class A {
   @Embedded
   private B b;

   // getter setter
}

Inside B class, I have a collection e.g.

@Embeddable
public class B {
    @OneToMany(fetch=FetchType.LAZY)
    @JoinColumn(name="A_ID")
    List<C> cList;
}

and class C

@Entity
public class C {
    // not important
}

Now I want to query A object using HQL and also retrieve the C list eagerly. I have tried

select a from A a join fetch a.b.c c

but I get this error Join fetch: “query specified join fetching, but the owner of the fetched association was not present in the select list”

I read this Join fetch: "query specified join fetching, but the owner of the fetched association was not present in the select list" and it seems that it is because my collection is stored inside an embedded object.

Of course I can do for loop and utilize Hibernate.initialize but the performance will drop when more data is being fetched.

Is it possible to do this in HQL at one shot?


Solution

  • It is possible to eager load the embedded collection using HQL. I believe you need to modify you HQL a bit.

    I have tested with following entities:

    NOTE: Added Multi to your entity names as I already have classes with same name A, B and C in my work space and added @Id annotation along with toString method etc.

    @Entity
    class MultiA {
        public MultiA() {}
    
        public MultiA(List<MultiC> cList) {
            this.b.cList = cList;
        }
    
        @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        public int id;
        @Embedded
    
        public MultiB b = new MultiB();
        @Override
        public String toString() {
            return "MultiA [id=" + id + ", b=" + b + "]";
        }
    
    }
    
    @Embeddable
    class MultiB {
        @OneToMany(fetch=FetchType.LAZY, cascade=CascadeType.ALL)
        @JoinColumn(name="A_ID")
        List<MultiC> cList = new ArrayList<MultiC>();
    
        @Override
        public String toString() {
            return "MultiB [cList=" + cList + "]";
        }
    
    }
    
    @Entity
    class MultiC {
        @Id
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        public int id;
    
        @Override
        public String toString() {
            return "MultiC [id=" + id + "]";
        }
    }
    

    And finally the HQL used is:

    session1.createQuery("select distinct a from MultiA a join fetch a.b.cList").list()
    

    This results in firing the below single SQL using join :

    Hibernate: select distinct multia0_.id as id1_0_0_, clist1_.id as id1_1_1_, clist1_.A_ID as A_ID2_1_0__, clist1_.id as id1_1_0__ from MultiA multia0_ inner join MultiC clist1_ on multia0_.id=clist1_.A_ID
    

    and gives the following output that returns 2 entities of MultiA along with MutliC populated.

    [MultiA [id=1, b=MultiB [cList=[MultiC [id=1], MultiC [id=2], MultiC [id=3]]]], 
    MultiA [id=2, b=MultiB [cList=[MultiC [id=6], MultiC [id=4], MultiC [id=5]]]]]