gustavomr
gustavomr

Reputation: 89

LazyLoadingException, Hibernate.initialize, FetchMode, MultipleBags

Imagine this relationshipt where a stock has many to many with category.

Stock

@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(name = "stock_category" , joinColumns = { 
        @JoinColumn(name = "STOCK_ID", nullable = false, updatable = false) }, 
        inverseJoinColumns = { @JoinColumn(name = "CATEGORY_ID", 
                nullable = false, updatable = false) })
public Set<Category> getCategories() {
    return this.categories;
}

Category

 @ManyToMany(fetch = FetchType.LAZY, mappedBy = "categories")
public List<Stock> getStocks() {
    return this.stocks;
}

When I tried to list all categories and your stocks I was getting Lazy Load exception - no session or session was closed.

So I changed my method to initialize all stocks from every category. If I want to initialize another entity I just put it inside for loop:

 session = HibernateUtil.getSessionFactory().openSession();
            Criteria cri = session.createCriteria(Category.class)
                     .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

            List<Category> categories = (List<Category>) cri.list();

            for (Category c :categories) {
                Hibernate.initialize(c.getStocks()); 
            } 

But I get an SQL for every Category to initialize stocks and I'm thinking this is not good.

So, I tried to use .setFetchMode("stocks", FetchMode.JOIN) and I'll be getting one SQL but if I want use fecthmode and need to join with another entity i'll getting "cannot simultaneously fetch multiple bags".

Product

@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(name = "product_category" , joinColumns = { 
        @JoinColumn(name = "PRODUCT_ID", nullable = false, updatable = false) }, 
        inverseJoinColumns = { @JoinColumn(name = "CATEGORY_ID", 
                nullable = false, updatable = false) })
public Set<Category> getCategories() {
    return this.categories;
}

List:

session = HibernateUtil.getSessionFactory().openSession();
            Criteria cri = session.createCriteria(Category.class)
                     .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
                     .setFetchMode("stocks", FetchMode.JOIN)
                     .setFetchMode("products", FetchMode.JOIN);

I'm not convinced about which is the best way to do this using hibernate. I read a lot about OpenSessionInView but some suggest that is a bad pattern.

Upvotes: 0

Views: 478

Answers (1)

Learner
Learner

Reputation: 21415

So I understand that your Category entity is having ManyToMany relationship with entities Stock and also Product. In your Category entity, if you try to use List for properties stocks amd products and then if you want to set the fetch mode to JOIN as :

`criteria.setFetchMode("stocks", FetchMode.JOIN).setFetchMode("products", FetchMode.JOIN);`

then Hibernate throws an exception as:

 org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags

To fix this issue you have to change the type of properties for stocks & products to Set instead of List in your Category entity class.

Upvotes: 2

Related Questions