user321068
user321068

Reputation:

LazyInitializationException when accessing collection in Runnable

I'm reading some entities with Hibernate:

Criteria criteria = session.createCriteria(…);
List<Entity> list = (List<Entity>) criteria.list();

Now I'm iterating over this list and want to send every object inside a Runnable to an Executor. I therefore use a RunnableBean.

for (Entity entity : list) {
    IRunnableBean runnableBean = (IRunnableBean) 
        applicationContext.getBean("myRunnableBean", IRunnableBean.class);
    runnableBean.setEntity(entity);
    executor.execute(runnableBean);
}

The RunnableBean looks like this:

RunnableBean implements IRunnableBean {

    // Setter

    @Transactional
    void run() {
        entity.getMyCollection();
    }
}

When I'm accessing the collection, I'm getting a org.hibernate.LazyInitializationException (no session or session was closed).

In Spring's log I see that the transactional method run() is correctly added. What am I doing wrong?

Upvotes: 0

Views: 1147

Answers (3)

bennidi
bennidi

Reputation: 2112

I would also guess (like @mindas) that the transaction is not available in your bean because it runs in a different thread than the one that holds the transaction. As far as my experience goes spring also uses thread locals to resolve scoped proxies, so these won't work either in a bean that is run asynchronously.

Basically I would try to avoid running logic that requires a transaction in an asynchronous fashion, since asynchronous calls run for a longer time (otherwise, why use async calls?) and this will block the transaction and/or lead to timeouts.

The criteria api from jpa offers ways to fetch a relation eagerly only for a specific query. Maybe that could be a choice? Otherwise accessing the size() method of a collection will initialize it.

Upvotes: 0

sp00m
sp00m

Reputation: 48837

Just add the following line within your already written for loop:

Hibernate.initialize(entity.getMyCollection());

This is load the collection eagerly instead of lazily: no LazyInitializationException anymore.

Upvotes: 0

mindas
mindas

Reputation: 26713

I guess you are using Spring's OpenSessionInViewFilter. If so, this behaviour is expected. Filter puts the database connection in the thread local context which is not available in your RunnableBean.

As myCollection isn't loaded eagerly, Spring does not have access to the database connection inside RunnableBean and can't load it. You need to:

  • create an enclosing session wrapper in your RunnableBean;
  • pass the id of your collection to the RunnableBean instead of passing object and load the collection inside RunnableBean

Alternatively, you can make your entity to load myCollection eagerly but this will make the overall loading process slower.

Upvotes: 2

Related Questions