2016-12-06 60 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