Hibernate の Session は、 Spring と一緒に利用するとき、基本的にはトランザクションが終了すると Session も閉じることになり、エンティティ(ドメインオブジェクト)と SessionFactory の同期が外れます(*1)。
*1 同期が外れていることを分離(detach)と言います。分離しているエンティティを分離オブジェクト(detached object)と言います。
Lazy Loading (レイジーローディング) を利用しているとき(これは Hibernate の default 設定)、トランザクション終了した(Session が閉じた)あとにエンティティのトランザクション開始時から実行していない getter を実行すると、以下のような LazyInitializationException が発生することになります。org.hibernate.LazyInitializationException: could not initialize proxy - no Session at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:86) at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:140) at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190) ...
そして、このままであると不都合があります。
- 画面で随時取りたい項目が取れない(画面では LazyLoading が利用できない)
- 画面間で同じドメインオブジェクトを利用するにしても再度取得しないといけない(画面間楽観排他)
- Open Session In View (OSIV) オープン・セッション・イン・ビュー
- Flow Managed Persistence (FMP) フロー・マネジド・パーシスタンス
1. の OpenSessionInView (OSIV) とは、 一度のリクエストからレスポンスの範囲で Session を開いたままにするという設計戦略です。この戦略には、 Spring Framework で用意している OpenSessionInViewFilter を利用すると別途実装なしに設定できます。不都合の 1. に対応します。
2. の Flow Managed Persistence (FMP) とは、複数画面を跨いで自由に設定した範囲で Session を開いたままにするという設計戦略です。この戦略には、 Spring Web Flow (SWF) を利用すると別途実装なしに設定できます。 SWF のスコープを利用してその範囲で Session を開いたままにします。不都合の 2. に対応します。
まとめると以下のとおりです。
戦略パターン | スコープ | 解決する問題 | 手段 |
---|---|---|---|
オープン・セッション・イン・ビュー (OSIV) | 1リクエスト (Session-per-request) | ビューのレンダリングで LazyLoading に関するエラーが出る | Spring で用意している SessionInViewFilter クラス |
フロー・マネジド・パーシスタンス(FMP) | 1カンバセーション(Session-per-conversation) | 画面やボタンを跨ぐ一連の処理(長い UnitOfWork)で、Session に手動でドメインオブジェクトを同期しないといけない(e.g. session.reflesh() )。 | Spring Web Flow |
もし SWF を使わないのなら、 FMP は自分で手動実装することになるでしょう。
また EJBサーバーを用意して JBoss Seam を使う場合なら、FMP同様のことができるかと思います。
つまり SWF を使う場合は、 FMP を使うと良いということになります。
実際の設定方法は次回へ。
T.B.C.
T.B.C.