Open Session in Viewは使用すべきなのか?
Spring BootでSpring Data JPAを使い始めた所、spring.jpa.open-in-view
に関する警告が表示されました。
これはOpen Session in Viewパターンに関する設定で、調べてみるとなかなか根深そうな問題でした。
この問題自体は古くからあるようですが、あまり情報がなかったので自分なりにまとめてみました。
Open Session in Viewとは
Spring Data JPAやHibernateではエンティティの遅延読み込みが可能となっている。
しかしViewでエンティティの詳細に初めてアクセスする場合、既にトランザクションが終了していると遅延読み込みが実行できない。
そこで、Viewでも参照できるようにと考えられたのが、Open Session in Viewパターン。
詳細はTERASOLUNAのガイドラインが分かりやすい。
Open Session in Viewに対する評判
Open Session in Viewは賛否両論あるようなので、見つけた意見を情報源とともにまとめました。
全体的には非推奨がやや多いでしょうか。
Web上の情報は意外にも少なかったです。
- ① Spring Boot:中立
- Auto Configurationによりデフォルトで有効になっているが、起動時に警告が表示される。
- 有効/無効はapplication.propertiesの
spring.jpa.open-in-view
で切り替えることができ、明示的に設定すると警告が表示されなくなる。 - このような挙動にしているのは、Open Session in Viewについて賛否両論あるためと推察される。
- Auto Configurationのソースを見ると、OpenEntityManagerInViewInterceptorを設定していることが分かる。
- ② TERASOLUNAのガイドライン:推奨
- OpenEntityManagerInViewInterceptorの使用を推奨、すなわちOpen Session In Viewに肯定的。
- 推奨の理由は、サービスクラスで一見無意味なデータアクセスが必要になるのを避けるため。
- ただしServlet Filter層ではLazy Fetchが発生しないようにすることを推奨している。
- ③ Spring Data JPAプログラミング入門(書籍):非推奨
- 書籍571ページにてOpen Session in Viewを非推奨とする旨が記述されている。
- この理由として、Viewでエラーハンドリングしなければいけなくなることが挙げられている。
- ④ Spring Data JPAによるデータアクセス徹底入門 #jsug(SlideShare):中立
- スライド107~108に注意点として記載されているが、推奨設定については言及されていない。
- 一方、スライド58では基本的には遅延読み込みを使うことが推奨されている。
- ⑤ @ITの記事 (Hibernateでインテグレーション層のDAOデザインを考える):非推奨
- 非推奨の理由として、Open Session in Viewはプレゼンテーション層がServletコンテナの場合にしか使えないことが挙げられている。
- 2006年の記事なので、今ほどWebアプリケーション一色ではなかったことも一因かもしれない。
- (個人的には、Spring FrameworkはWebアプリ以外でも使えるのがメリットの一つであると捉えているので、この主張は妥当だと考える)
- ⑥ Java EE勉強会(議事録):非推奨
- 使うべきではない。
- その理由として、プレゼンテーション層がデータアクセス層に依存するのはおかしいこと、プレゼンテーション層でのエラーハンドリングが厄介であることなどが挙げられている。
番外編として、Spring Bootをはじめる時にやるべき10のことでは、そもそもHibernateはハマりやすいのであまり推奨していないような印象を受けます。
Open Session in Viewを使わないために
自分なりに考えてみましたが、Open Session in Viewは使うべきでないと判断しました。
本質的に、エラーハンドリングやデータベースアクセスはサービスクラスで完結すべきと思います。
また副次的な理由として、SpringはWebアプリ以外でも使えるフレームワークなのだから、サーブレットに限定されない構成を適用したほうがよいと思いました。
ところで非推奨の意見は多かったものの、Open Session in Viewを使わない場合にどうすれば良いかはあまり言及されていませんでしたが、実際には2つの方法があると思います。
一つはLazy Fetchを使う方法。Lazy Fetchなので、大量のデータを読み込んでしまうリスクは減ります。
ただしサービスクラスでgetterを呼び出したり、コレクションにアクセスしたりと、やや本質的でないコードを記述する必要があり、コードの可読性も低下します。
もう一つはLazy Fetchを使わない方法。
具体的には@OneToManyなどのフィールドにEager Fetchを適用することになります。
ただしこれはコードが簡潔になる代わりに、大量のデータを読み込んでしまうリスクがあります。
読み込むデータ量が限定的であればEager Fetchでも構わないでしょうし、そもそも@OneToManyのリレーション自体を減らすことも重要だと思いました。
まとめ
アーキテクチャの本質的な観点から、Open Session in Viewは採用すべきでないと判断しました。
Lazy Fetchを使うかどうかは悩ましい所ですが、できるだけLazy Fetchを使わないほうがコードが簡潔になり、開発のスピードを高められるのが魅力だと思います。
そのためにはできるだけ@OneToManyリレーションを使わず、大量のデータを読み込んでしまうリスクを抑えていくことが重要になりそうです。
この問題の根幹はインピーダンスミスマッチにあると思いますし、SQLとオブジェクト指向を使う限り、どこかで折り合いをつける必要がありそうです。