#freeze
[[FrontPage]]

#contents

2008/03/07からのアクセス回数 &counter;

* HibernateのLazy initializeエラーでビューを表示できない [#vfb7a9f6]

Cart問題の最後で、注文者の情報をいれた後に内容を表示するときに以下のようなエラーが発生しました。
こんな最後の最後に勘弁してくださいと気持ちでした!

#pre{{
-- 例外のトレース
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is 
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: 
example.cart.domain.Order.lineItemList, no session or session was closed
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:488)
	org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:431)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:689)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
	org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:96)
	org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75)

-- 本来の原因
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: 
example.cart.domain.Order.lineItemList, no session or session was closed
	org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:358)
--途中省略
	org.apache.velocity.Template.merge(Template.java:254)
--途中省略
	org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1158)
--途中省略
	org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:431)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:689)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
	org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:96)
	org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75)
}}

どうやらビューでコレクションの要素であるLineItemを遅延初期化(lazily initialize)しようとした時に、
Hinbernateのsessionがクローズしていたので例外が発生みたいです。

そこで、ネット"Spring Hibernate Lazy initialize error" をキーに検索すると
- [[とあるITエンジニアの業務手帖>http://d.hatena.ne.jp/getbean/]]

に同様のケースが報告されていました。

解決策としては、''Hibernateマッピングファイルのlazy属性をfalseにする''というものでした。
そこで、以下のようにOrderのbagタグにlazy="false"を入れ
#pre{{
		<bag name="lineItemList"
			cascade="all"
			table="T_LINEITEM"
			lazy="false">
			<key column="orderId" foreign-key="ID"/>
			<one-to-many class="example.cart.domain.LineItem"/>
		</bag>
}}

LineItemのmany-to-oneタグにも同様に
#pre{{
		<many-to-one
			name="product"
			column="productId"
			cascade="save-update"
			lazy="false"
			class="example.cart.domain.Product"/>
}}

としたら、正常に動作するようになりました。

** Hibernateがデフォルトでlazy initializeをtrueにしている理由 [#j169b21b]
では、どうしてHibernateがデフォルトでlazy initializeをtrueにしているのでしょう。

それは、パフォーマンスを向上するため不要な初期化をさけるためにlazy initializeをtrueにしているのだと思われます。

とするとlazy="false"の設定は、本当の対処方法ではないのではないかと考え、どこかに情報がないか調べました。

今度は、"org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: "で検索すると

#pre{{
Originally Posted by karldmoore  
If the association is lazy loaded and the Session has been closed then this won't work. 
You either need to eagerly load the association, use something like OpenSessionInView to 
extend the Session around the request or reattach the entity to the Session before trying to lazy load.
}}

という回答を見つけました。

どうやら、OpenSessionInViewを使えば、クローズしていたsessionでlazy loadができるみたいです。

- [[新・たけぞう瀕死の日記>http://www3.vis.ne.jp/~asaki/p_diary/diary.cgi?Date=20040615]]

にOpenSessionInViewの使い方が出ていました。

また、Spring LIVEの240ページにも同様の説明がでていました。

** 私の解決策 [#z334569d]
OpenSessionInViewには、批判的なコメントも見受けられました。

それは、
- HTTPのセッションで同一のHinbernateのsessionを使用しなくてはならない
- lazy initializeでのflashModeをAUTOにしなくてならない

上記の問題を考え合わせた結果、
- Hibernateマッピングファイルのlazy=falseの部分を元に戻し
- 以下のようにweb.xmlにOpenSessionInViewのフィルターを追加しました。
#pre{{
	<filter>
		<filter-name>hibernateFilter</filter-name>
		<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
		<init-param>
			<param-name>singleSession</param-name>
			<param-value>true</param-value>
		</init-param>
		<init-param>
			<param-name>flushMode</param-name>
			<param-value>AUTO</param-value>
		</init-param>
	</filter>
	
	<filter-mapping>
		<filter-name>hibernateFilter</filter-name>
		<url-pattern>*.htm</url-pattern>
	</filter-mapping>
}}

filterのinit-paramでsingleSessionとflushModeを指定すると方法がベストだと思います。

* コメント [#w7e5aec0]
この記事は、

#vote(おもしろかった[18],そうでもない[0],わかりずらい[0])
#vote(おもしろかった[42],そうでもない[0],わかりずらい[1])

皆様のご意見、ご希望をお待ちしております。

#comment_kcaptcha


トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
SmartDoc