Hibernate fetch join -> cannot fetch multiple bags

18,661

Solution 1

The answer is: no. It can't handle it. That's what it says.

For value types (composite-element) it wouldn't even work, because you don't get the information what actually belongs to the same bag item.

Usually it doesn't even make sense. If you query a table and get 10 records in the starting table, 10 in the first bag and another 10 in the second bag, you'll retrieve 1000 records just to create these 30 objects in memory. Imagine the number of records when there would be 100 records in each table (hint: 1,000,000 instead of 300) and when you fetch join another bag (hint: 100,000,000 instead of 400) ...

By the way: join fetch may lead to strange effects and problems and should be avoided, except you exactly know what you are doing.

Solution 2

Instead of using Set, you can split the query and load the entities in different queries.
Eg

    From PointOfInterest p left join fetch p.labels WHERE p.figure = :figure
    From PointOfInterest p left join fetch p.tags WHERE p.figure = :figure            

Please refer the link

Share:
18,661
mkuff
Author by

mkuff

Updated on July 03, 2022

Comments

  • mkuff
    mkuff almost 2 years

    Problem is i have two bags in my entity which i would like to display in my jsf frontend (Spring in the back so no lazy loading). So i have to eagerly fetch them to display the information in a list like this:

    • Point 1 (Label 1, Label 2) (Tag1 ... Tag n)
    • Point 2 (Label 3, Label 4) (Tag1 ... Tag n)

    Putting both Lists to eager didn't work. So i tried my luck with a fetch join. It allowed me to fetch one list, but when i added the second list i get the known "cannot fetch multiple bags" error.

    Can Hibernate handle two fetch joins in a query?

    public class PointOfInterest
     @OneToMany(mappedBy="poi")
    private List<PointOfInterestLabel> labels = new ArrayList<PointOfInterestLabel>();
    
    @ManyToMany
    private List<Tag> tags = new ArrayList<Tag>();
    

    My fetch join:

    SELECT DISTINCT p from PointOfInterest p 
            left join fetch p.labels 
            left join fetch p.tags WHERE p.figure = :figure
    

    On startup the creation of my hibernate factory fails with:

    Caused by: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
        at org.hibernate.loader.BasicLoader.postInstantiate(BasicLoader.java:94)
        at org.hibernate.loader.hql.QueryLoader.<init>(QueryLoader.java:123)
        at org.hibernate.hql.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:206)
        at org.hibernate.hql.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:136)
        at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:101)
        at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:80)
        at org.hibernate.engine.query.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:98)
        at org.hibernate.impl.SessionFactoryImpl.checkNamedQueries(SessionFactoryImpl.java:557)
        at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:422)
        at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1385)
        at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:954)
        at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:883)
        ... 55 more
    
  • mkuff
    mkuff over 12 years
    Thanks! For the moment i replaced it with Set, but i think i will refactor the interface tomorrow and display fewer information. Thanks for the feedback! How would you solve it when you have no persistence scope in your frontend. Use Data Transfer Objects built in the DAO instead of fetched entities? I inherited the project and i am not allowed to make huge design changes, nor am i allowed to switch it to j2ee. :-/
  • Stefan Steinegger
    Stefan Steinegger over 12 years
    You could turn off lazy loading, which is bad for performance. This fetches the bags immediately by separate queries. You could also access the bags (eg. size) to force loading. The perfect solution is to create the session outside the dao and keep it for a whole business transaction. (Creating a session in each call to the dao is called session-per-call and is an anti-pattern.)
  • Deroude
    Deroude over 7 years
    So really this is a catch 22 problem: if you don't use join fetch, you get n+1 problem or lazy initialization exception. Session outside dao is a hack, first of all because it is nowhere near the JPA contract and second of all because it is hardly controllable, since you shouldn't make any assumptions on the lifetime of an entity at the moment where you create it. It might span over several requests, not necessarily http.
  • Stefan Steinegger
    Stefan Steinegger over 7 years
    You can avoid N+1 with the great batch-fetching feature, which is transparent and doesn't have any side effects. You don't need to reference the session from outside the dal, but you have to control the life time of the transaction from outside of the dal, because it is longer than a single call to the db. You can hide it behind a dal interface. In our project, we've implemented a environment transaction thing, which is stored in a thread static field in the dal.