在Runnable中访问集合时的LazyInitializationException

时间:2012-11-08 13:58:54

标签: java multithreading spring hibernate spring-transactions

我正在阅读Hibernate的一些实体:

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

现在我正在迭代此列表,并希望将Runnable内的每个对象发送到Executor。因此,我使用RunnableBean

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

RunnableBean看起来像这样:

RunnableBean implements IRunnableBean {

    // Setter

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

当我访问该集合时,我收到org.hibernate.LazyInitializationExceptionno session or session was closed)。

在Spring的日志中,我看到正确添加了事务方法run()。我做错了什么?

3 个答案:

答案 0 :(得分:2)

我猜你正在使用Spring's OpenSessionInViewFilter。如果是这样,则会出现此行为。 Filter将数据库连接放在线程本地上下文中,这在RunnableBean

中不可用

由于myCollection未被急切加载,因此Spring无法访问RunnableBean内的数据库连接,无法加载它。你需要:

  • RunnableBean;
  • 中创建一个封闭的会话包装器
  • 将您的收藏集的ID传递给RunnableBean,而不是传递对象并加载RunnableBean
  • 内的集合

或者,您可以让您的实体热切地加载myCollection,但这会使整个加载过程变慢。

答案 1 :(得分:0)

只需在已编写的for循环中添加以下行:

Hibernate.initialize(entity.getMyCollection());

这会急切地加载集合而不是懒惰:不再LazyInitializationException

答案 2 :(得分:0)

我也猜测(比如@mindas)你的bean中的事务是不可用的,因为它运行在与持有事务的线程不同的线程中。根据我的经验,spring还使用线程本地来解析范围内的代理,因此这些代码在异步运行的bean中都不起作用。

基本上我会尽量避免以异步方式运行需要事务的逻辑,因为异步调用运行的时间较长(否则,为什么要使用异步调用?)这会阻止事务和/或导致超时。

来自jpa的标准api提供了仅针对特定查询急切地获取关系的方法。也许这可能是一个选择?否则,访问集合的size()方法将初始化它。