0

我正在使用Hibernate阅读一些实体:

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

现在我正在遍历这个列表,并希望将 a 中的每个对象发送RunnableExecutor. 因此,我使用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()正确添加了事务方法。我究竟做错了什么?

4

3 回答 3

2

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

由于myCollection没有预先加载,Spring 无法访问内部的数据库连接RunnableBean,也无法加载它。你需要:

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

或者,您可以使您的实体快速加载myCollection,但这会使整个加载过程变慢。

于 2012-11-08T14:22:31.850 回答
0

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

Hibernate.initialize(entity.getMyCollection());

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

于 2012-11-08T14:40:00.970 回答
0

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

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

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

于 2012-11-08T15:00:20.023 回答