Hibernate and CriteriaAPI - Loading Lists in cascade, Eagerly with performance

Problem

I have the object A, which contains a list of B and B contains a list of C.
I need to load everything from a certain A.

Using Join Fetch is a no-no because of the duplicates.

Also see here: The best way to fix the Hibernate MultipleBagFetchException

Also from the same link, you will notice that making everything a Set so you can join fetch is also bad for performance.


Current Inneficient solution

The current solution proposes that you load A using an ID and initialize the B list using Hibernate.initialize and for each B loaded, apply Hibernate.initialize to load C list.

Its inneficient per nature. However, I dont know how to load everything up using a single query.

    @Override
        public A find(Long id, Session session, boolean closeSession) {
            
            try {
             
                CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
    
                CriteriaQuery<A> queryA = criteriaBuilder.createQuery(A.class);
    
                Root<A> root = queryA.from(A.class);
    
                queryA.select(root);
                queryA.where(criteriaBuilder.equal(root.get("id"), id));
               
                Query<A> query = session.createQuery(queryA);
    
                A singleResult = query.getSingleResult();
    
                //Initialize The Bs List
                Hibernate.initialize(singleResult.getBs());
                
                //Initialize each Cs List from each B
                for(B b : singleResult.getBs()){
                    Hibernate.initialize(b.getCs());
                }
                
                return singleResult;
    
            } catch (Exception ex) {
                throw ex;
            } finally {
                if (session != null && closeSession) {
                    session.close();
                }
            }
        }

Question

How to load everything from A(id) with performance ?


    @Entity
    public class A {
        @Id
        @GeneratedValue
        private Long id;
    
        private String Name;
    
        @OneToMany(fetch = FetchType.LAZY)
        private Set<B> bs;
    }
    
    
    @Entity
    public class B {
        @Id
        @GeneratedValue
        private Long id;
    
        private String Name;
    
        @OneToMany(fetch = FetchType.LAZY)
        private Set<C> cs;
    }
    
    
    @Entity
    public class C {
        @Id
        @GeneratedValue
        private Long id;
    
        private String Name;
    }

Join fetching everything is only a performance problem if the cardinality of cs is very high. You can also use @Fetch(FetchMode.SUBSELECT) to fetch all cs in one lazy initialization query.

1 Like