2012年6月18日月曜日

Hibernate Session スコープ戦略 1/3 (理論編)

Hibernate を利用した場合のドメインオブジェクトの同期範囲の扱いについて、日本語リソースが少ないため、ここで解説しておきます。 ブログという都合上かなり簡略化して書いている部分もあります。 Hibernate の Session のスコープをどう扱うかという戦略(Session スコープ戦略 / Session 引き回し戦略 とでも言いましょうか)についてです。 Hibernate + Spring を基本に書いています。

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)
...

 そして、このままであると不都合があります。
  1. 画面で随時取りたい項目が取れない(画面では LazyLoading が利用できない)
  2. 画面間で同じドメインオブジェクトを利用するにしても再度取得しないといけない(画面間楽観排他)
もし分散処理で業務処理と画面処理が SOAP などのプロトコルを介して分かれているなら、事前に getter を動かしておく、 Eager Fetch で取得しておくなどして、甘んじて受け入れることになりますが、同じ筐体であれば解決したいところです。 すなわち、ドメインオブジェクトの値を取得したいその間に Session を開いたままにしておくことができれば、この問題は解決できます。Hibernate の Session を開いたまま引き回すということです。 Hibernate の Session を引き回す戦略は、2つあります。
  1. Open Session In View (OSIV) オープン・セッション・イン・ビュー
  2. 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. に対応します。 まとめると以下のとおりです。
Hibernate Session のスコープ戦略パターン
戦略パターン スコープ 解決する問題 手段
オープン・セッション・イン・ビュー (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.

0 件のコメント:

コメントを投稿

Spotlight 検索結果のパスを見る方法

Spotlight 検索結果のパスを見る方法 ============================================ Mac Spotlight 検索結果について。 問題 -------------- 1. 検索結果のファイルは直接開くことができる. 1....