【发布时间】:2017-04-21 00:14:20
【问题描述】:
我在生产环境中部署了一个 Grails 应用程序 (2.5.4),它接收大量流量。
在尝试访问存储在会话中的域对象中的字段时,我们遇到了间歇性的 LazyInitializationException 异常。
阐明流程的工作原理:
我们有一个过滤器 (http://docs.grails.org/2.5.4/ref/Plug-ins/filters.html),它在每个控制器操作之前被调用。在这个过滤器中,我们在会话 (http://docs.grails.org/2.5.4/ref/Servlet%20API/session.html) 中存储一个域对象,如下所示:
session.account = Account.get(1)
在控制器中,我们像这样检索域:
def account = session.account
然后我们将域对象传递给另一个服务,该服务调用另一个服务,该服务最终尝试调用域对象上的 hasMany 字段,如下所示:
account.transactions.name
上面会抛出一个LazyInitializationException,并带有类似这样的消息:
failed to lazily initialize a collection of role: com.example.app.Account.transactions, no session or session was closed
因此,由于某种原因,Hibernate 会话在请求完成之前被关闭,因此出现延迟加载异常。
我们发现在控制器中执行以下操作完全消除了错误的发生:
Account account = Account.findById(session.account.id)
问题是,在盲目地在应用程序的其他部分实施此修复之前,我不知道为什么并且想了解为什么这会解决问题。我看不出为什么该对象应该与 Hibernate Session 分离,因为这个流程都发生在同一个请求中。最重要的是,这是一个非常随机的问题——它可能会出现在请求发出的 1% 的时间里,如果不是更少的话。
为了澄清,问题是;为什么会话被关闭,为什么当对象都发生在同一个请求范围内时,它会与 Hibernate 会话分离?另外,为什么它只是非常罕见且随机地发生?
【问题讨论】:
-
在尝试访问
account.transactions之前,您是否尝试过使用account.attach()?见docs.grails.org/latest/ref/Domain%20Classes/attach.html -
嘿 - 我没有特别尝试过,但我想它会像
Account account = Account.findById(session.account.id)一样工作。我的问题不是如何将域重新附加到会话,因为这很容易,而是为什么它首先被分离。您提供的链接中的文档指出:“如果从 Session 中检索到一个对象并将其放入诸如 HttpSession 之类的 Web 范围内,那么一旦 Session 关闭并丢弃,它将与 Hibernate Session “分离”。那么,如果请求没有完成,为什么会话会关闭并丢弃? -
Grails 有一个 Slack 频道,专家们往往会在那里闲逛。 StackOverflow 更适合“如何...”问题,所以我认为你会在 Slack 或 Grails 邮件列表中取得更好的成功。
-
谢谢 - 我已经在 slack 频道问过了。如果我得到回复,我会报告。
标签: spring hibernate grails lazy-loading httpsession