【问题标题】:LazyInitializationException when accessing collection in Runnable访问 Runnable 中的集合时出现 LazyInitializationException
【发布时间】:2012-11-08 13:58:53
【问题描述】:

我正在阅读一些带有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.LazyInitializationException (no session or session was closed)。

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

【问题讨论】:

    标签: java multithreading spring hibernate spring-transactions


    【解决方案1】:

    我猜你正在使用Spring's OpenSessionInViewFilter。如果是这样,这种行为是预期的。过滤器将数据库连接置于您的RunnableBean 中不可用的线程本地上下文中。

    由于myCollection 没有立即加载,Spring 无法访问RunnableBean 内部的数据库连接,因此无法加载它。您需要:

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

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

    【讨论】:

      【解决方案2】:

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

      Hibernate.initialize(entity.getMyCollection());
      

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

      【讨论】:

        【解决方案3】:

        我还猜测(如@mindas)事务在您的 bean 中不可用,因为它在与持有事务的线程不同的线程中运行。就我的经验而言,spring 还使用线程局部变量来解析作用域代理,因此这些在异步运行的 bean 中也不起作用。

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-04-04
          • 1970-01-01
          • 1970-01-01
          • 2013-10-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多