欢迎进入Wiki » FAQ » 为什么调用 Domain.current().applications 会抛出 LazyInitializationException 异常?

为什么调用 Domain.current().applications 会抛出 LazyInitializationException 异常?

在2013-11-13 09:00上被李小翔修改
评论 (0) · 附件 (0) · 记录 · 信息

问题代码:

import bropen.framework.core.security.Domain;

// 获得当前管理域
Domain domain = Domain.current()
// 使用当前域下的所有应用进行查询,会抛出异常!!
ProcessDefinition.findAllNotDisabledByApplicationInList(domain.applications, [cache:true])

抛出异常:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role:
    bropen.framework.core.security.Domain.applications, no session or session was closed
    ... 13 more

在错误日志上加上 withTransaction 或者 withNewSession 都不管用,该如何处理呢?

其原因在于,bropen.framework.core.security.Domain.current()(包括DomainApplication.current())拿到的实体Bean都是来自缓存,而这些缓存可能是早在被其他代码(比如BootStrap)调用时生成的,当时的Hibernate Session早已关闭,如果通过这些Bean去获得关联的其他实体对象,肯定会抛出no session or session was closed异常。

知道原因后,解决问题就很容易了,可以采用两种方式来处理:

1. 通过获得实体对象后,调用 attach 方法将对象附加到当前的Hibernate Session中,代码如:

if ( !bean.isAttached() ) {
    bean.attach()
}

这种处理方式适用与多数类似的异常,但是如果这个bean可能同时被多个线程调用,这样做会不会导致线程不安全的情况发生呢(细心的读者可以验证一下)。

因此,比较偷懒的做法是采用下面的方案2来规避这个问题。

2. 接受这个bean游离在当前Hibernate Session之外的现实,通过其他手段来获取关联的其他实体对象,就最上面的问题代码来说,改造如下:

import bropen.framework.core.security.Domain;
import bropen.framework.core.security.DomainApplication;

// 从缓存中获得当前管理域对象
Domain domain = domain.current();
// 加一行,避免session关闭问题
List<DomainApplication> apps = DomainApplication.findAllByDomain(domain);
// 这回就不会异常了
ProcessDefinition.findAllNotDisabledByApplicationInList(apps, [cache:true]);

当然,为什么不把关联的 applications 也缓存下来呢?

呵呵,那缓存的工作量就比较大了,整个系统的数据结构都有可能相互关联,谁也无法预料到二次开发时中会如何调用,这样做意义并不大;而且,如果关联的对象发生了变化,还得通知缓存进行更新......

类似的,如果碰到类似的异常,也可以采用相同的方法处理。

在2013-11-13 08:59上被李小翔创建

Copyright © 2013 北京博瑞开源软件有限公司
京ICP备12048974号