我在生产中部署了一个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 Session中分离出来?另外,为什么它只发生很少和随机?
在尝试访问'account.transactions'之前,您是否尝试过使用'account.attach()'?请参阅http://docs.grails.org/latest/ref/Domain%20Classes/attach.html –
嘿 - 我没有特别尝试过,但我会想像它会像“Account account = Account”一样工作。 findById(session.account.id)'。我的问题不在于如何重新将域添加到会话中,因为这很容易,但是为什么它首先被分离。您提供的链接中的文档状态如下: “如果从会话中检索到对象并将其放入Web范围(如HttpSession),则会在Session关闭并丢弃时从Hibernate Session”分离“。 那么,如果请求尚未完成,为什么会话关闭并丢弃? – DeaIss
Grails有一个Slack频道,大师们倾向于闲逛。 StackOverflow更适合“如何...”的问题,所以我认为您在Slack或Grails邮件列表中会有更好的成功。 –