2016-12-06 100 views
1

我在生产中部署了一个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中分离出来?另外,为什么它只发生很少和随机?

+0

在尝试访问'account.transactions'之前,您是否尝试过使用'account.attach()'?请参阅http://docs.grails.org/latest/ref/Domain%20Classes/attach.html –

+0

嘿 - 我没有特别尝试过,但我会想像它会像“Account account = Account”一样工作。 findById(session.account.id)'。我的问题不在于如何重新将域添加到会话中,因为这很容易,但是为什么它首先被分离。您提供的链接中的文档状态如下: “如果从会话中检索到对象并将其放入Web范围(如HttpSession),则会在Session关闭并丢弃时从Hibernate Session”分离“。 那么,如果请求尚未完成,为什么会话关闭并丢弃? – DeaIss

+0

Grails有一个Slack频道,大师们倾向于闲逛。 StackOverflow更适合“如何...”的问题,所以我认为您在Slack或Grails邮件列表中会有更好的成功。 –

回答

0

你好,这是因为懒惰模式将缓存只有第一级的域,所以当你试图访问其他字段会给你这个错误和默认的获取模式通常是懒惰的,我会保持这样和按照你所做的, 帐户帐户= Account.findById(session.account.id)

因为根据对方列表的大小将所有内容放入会话中对您的系统根本不会有好处。

+0

我知道域使用延迟加载,但是如果Hibernate Session仍然打开,那么它应该能够获取该域的任何更高级别的关系。所以问题是,为什么hibernate会话会随机关闭,为什么当请求还没有结束时域对象会分离? – DeaIss