Hibernate Open Session in Viewが悪い習慣と見なされるのはなぜですか?


107

そして、LazyLoadExceptionsを回避するためにどのような代替戦略を使用しますか?

表示中の開いているセッションに次の問題があることを理解しています:

  • 異なるjvmで実行されている階層化アプリケーション
  • トランザクションは最後にのみコミットされ、おそらく前に結果が欲しいでしょう。

しかし、アプリケーションが単一のvmで実行されていることがわかっている場合は、オープンセッションインビュー戦略を使用して痛みを和らげてみませんか?


12
OSIVは悪い習慣と考えられていますか?誰によって?
Johannes Brodwall 2009

4
そして-良い選択肢は何ですか?
David Rabinowitz、

7
Seam開発者からの場合のこのテキストの平安:この実装にはいくつかの問題があります。最も深刻なのは、トランザクションがコミットされるまでトランザクションが成功することを確信できないことですが、「ビュー内のオープンセッション」トランザクションがコミットされるまでは、ビューは完全にレンダリングされ、レンダリングされた応答はすでにクライアントにフラッシュされている可能性があります。トランザクションが失敗したことをユーザーにどのように通知できますか?
darpet


2
長所と短所、および私自身の経験については、このブログ投稿を参照してください-blog.jhades.org/open-session-in-view-pattern-pros-and-cons
Angular University

回答:


46

おそらく初期化されていないプロキシ、特にコレクションをビューレイヤーで送信し、そこから休止状態の読み込みをトリガーすると、パフォーマンスと理解の両方の観点から問題が発生する可能性があるためです。

理解

OSIVを使用すると、データアクセスレイヤーに関連する問題でビューレイヤーが「汚染」されます。

ビューレイヤーはHibernateException遅延読み込み時に発生する可能性のあるを処理する準備ができていませんが、おそらくデータアクセスレイヤーはそうです。

パフォーマンス

OSIVはカーペットの下で適切なエンティティの読み込みを引っ張る傾向があります-コレクションまたはエンティティが遅延して初期化されている(おそらくN + 1)ことに気付かない傾向があります。利便性が高く、コントロールが少ない。


更新:この主題に関するより大きな議論については、OpenSessionInViewアンチパターンを参照してください。著者は3つの重要なポイントをリストします。

  1. 各遅延初期化はクエリを取得します。これは、各エンティティがN + 1クエリを必要とすることを意味します。ここで、Nは遅延関連付けの数です。画面に表形式のデータが表示されている場合、Hibernateのログを読むことは、本来行うべきではない大きなヒントです
  2. プレゼンテーションレイヤーでDBを使用して爪を汚すため、これはレイヤードアーキテクチャを完全に無効にします。これは概念的な詐欺なので、私はそれと共存できますが、当然の結果があります
  3. 最後に重要なことですが、セッションのフェッチ中に例外が発生すると、ページの書き込み中に例外が発生します。ユーザーにクリーンなエラーページを提示することはできず、実行できるのは、本文にエラーメッセージを書き込むことだけです。

13
さて、休止状態の例外を除いて、ビューレイヤーを「汚染」します。しかし、パフォーマンスに関しては、問題はあなたのdtoを返すサービス層にアクセスすることと非常に似ていると思います。パフォーマンスの問題に直面した場合は、より具体的な問題を、よりスマートなクエリまたはより軽量なdtoで最適化する必要があります。ビューで必要となる可能性を処理するためにあまりにも多くのサービスメソッドを開発する必要がある場合は、サービスレイヤーも「汚染」しています。番号?
HeDinges 2009

1
違いの1つは、Hibernateセッションの終了が遅れることです。JSPがレンダリング/書き込みなどされるまで待機します。これにより、オブジェクトがメモリに長く保持されます。これは、特にセッションコミット時にデータを書き込む必要がある場合に問題になる可能性があります。
ロバートムンテアヌ

8
OSIVがパフォーマンスを低下させると言っても意味がありません。DTOを使用する以外にどのような選択肢がありますか?その場合、ビューで使用されるデータは、それを必要としないビューでもロードする必要があるため、常にパフォーマンスが低下します。
ヨハネスBrodwall 2009

11
汚染は逆方向に作用すると思います。データを積極的にロードする必要がある場合、ロジックレイヤー(またはさらに悪い場合はデータアクセスレイヤー)は、オブジェクトがどのように表示されるかを知る必要があります。ビューを変更すると、不要なものや不要なオブジェクトがロードされてしまいます。Hibernate Exceptionはバグであり、他の予期しない例外と同じように有害です。しかし、パフォーマンスは問題です。パフォーマンスとスケーラビリティの問題により、データアクセスレイヤーでより多くの検討と作業が必要になり、場合によってはセッションをより早く閉じる必要があります
Jens Schauder

1
@JensSchauder「ビューを変更すると、不要なものをロードするか、必要なオブジェクトが見つからない」これはまさにそれです。ビューを変更する場合は、ビューをロードさせるよりも、不要なものをロードするか(積極的にフェッチする可能性が高いため)、欠落しているオブジェクトを見つけて、遅延ロードの例外が発生するようにする方がはるかに優れています。 N + 1の問題が発生し、それが発生していることに気付くこともないため、遅延してください。したがって、IMOの方がサービスレイヤー(およびユーザー)は、ビューの遅延読み込みよりも何が送信されるかをよく理解していて、何も知りません。
Jeshurun、2011年

40

より詳しい説明については、私の「Open Session In View Anti-Pattern」の記事をご覧ください。それ以外の場合、Open Session In Viewを使用してはならない理由の概要を次に示します。

Open Session In Viewは、データのフェッチに悪いアプローチをとっています。ビジネスレイヤーに、ビューレイヤーに必要なすべての関連付けを取得するのに最適な方法を決定させる代わりに、永続化コンテキストを開いたままにして、ビューレイヤーがプロキシの初期化をトリガーできるようにします。

ここに画像の説明を入力してください

  • OpenSessionInViewFilter呼び出してopenSession根底にあるの方法SessionFactoryと新しいを取得Session
  • SessionにバインドされていますTransactionSynchronizationManager
  • OpenSessionInViewFilter呼び出しdoFilterjavax.servlet.FilterChainオブジェクト参照を、要求がさらに処理されます
  • DispatcherServlet根本的にHTTPリクエストと呼ばれ、ルートされますPostController
  • はをPostController呼び出してエンティティのPostServiceリストを取得しますPost
  • PostService新しいトランザクションを開き、HibernateTransactionManager同じ再利用Sessionによって開かれたものOpenSessionInViewFilter
  • は、遅延関連付けを初期化せずPostDAOPostエンティティのリストをフェッチします。
  • PostService基礎となるトランザクションをコミットしますが、Sessionそれは外部に開かれたために閉鎖されていません。
  • DispatcherServlet今度は、遅延関連付けをナビゲートし、その初期化をトリガー、UIを、レンダリングを開始。
  • OpenSessionInViewFilterを閉じることができSession、基になるデータベース接続も解放されます。

一見すると、これはひどいことのようには見えないかもしれませんが、データベースの観点から見ると、一連の欠陥がより明白になります。

サービス層はデータベーストランザクションを開いたり閉じたりしますが、その後は明示的なトランザクションは発生しません。このため、UIレンダリングフェーズから発行されるすべての追加のステートメントは、自動コミットモードで実行されます。各ステートメントはトランザクションログをディスクにフラッシュする必要があるため、自動コミットはデータベースサーバーに負荷をかけ、データベース側で大量のI / Oトラフィックを引き起こします。最適化の1つはConnection、データベースサーバーがトランザクションログへの書き込みを回避できるように、読み取り専用としてマークすることです。

ステートメントはサービスレイヤーとUIレンダリングプロセスの両方で生成されるため、問題の分離はもうありません。生成されるステートメントの数表明する統合テストを作成するには、アプリケーションをWebコンテナーにデプロイしながら、すべてのレイヤー(Web、サービス、DAO)を通過する必要があります。インメモリデータベース(HSQLDBなど)と軽量Webサーバー(Jettyなど)を使用している場合でも、これらの統合テストは、レイヤーが分離されてバックエンド統合テストがデータベースを使用する場合よりも実行が遅くなります。フロントエンド統合テストは、サービス層を完全に模倣していました。

UIレイヤーは、関連付けをナビゲートすることに限定されており、関連付けがN + 1クエリの問題を引き起こす可能性があります。Hibernateは@BatchSize関連付けをバッチでフェッチすることを提供しFetchMode.SUBSELECT、このシナリオに対処するために、アノテーションはデフォルトのフェッチプランに影響を与えるため、すべてのビジネスユースケースに適用されます。このため、現在のユースケースのデータフェッチ要件に合わせて調整できるため、データアクセスレイヤークエリの方がはるかに適しています。

最後に重要なことですが、データベース接続は(接続解放モードに応じて)UIレンダリングフェーズ全体で保持される可能性があり、接続リース時間を増加させ、データベース接続プールの輻輳による全体的なトランザクションスループットを制限します。接続が保持されるほど、他の同時要求はプールから接続を取得するために待機することになります。

したがって、接続を長時間保持するか、単一のHTTPリクエストに対して複数の接続を取得または解放するため、基礎となる接続プールに圧力をかけ、スケーラビリティを制限します。

春のブーツ

残念ながら、Spring BootのOpen Session in Viewはデフォルトで有効になっています

したがって、application.properties構成ファイルに次のエントリがあることを確認してください。

spring.jpa.open-in-view=false

これによりOSIVが無効になりLazyInitializationException、正しい方法で処理できるようになります。


3
ビューのオープンセッションを自動コミットで使用することは可能ですが、Hibernate開発者が意図した方法ではできません。したがって、[Open Session in View]には欠点がありますが、自動コミットは、オフにしてそのまま使用できるため、1つではありません。
stefan.m

あなたはトランザクションの中で何が起こるかについて話している、そしてそれは本当です。ただし、Webレイヤーのレンダリング段階はHibernateの外部で行われるため、自動コミットモードになります。理にかなっていますか?
Vlad Mihalcea

これは、Open Session in Viewに最適ではない変形だと思います。セッションとトランザクションは、ビューがレンダリングされるまで開いたままにしておく必要があります。そうすると、自動コミットモードは必要ありません。
stefan.m

2
セッションは開いたままです。しかし、トランザクションはしません。プロセス全体にトランザクションをスパンすることも、トランザクションの長さを増やし、ロックが必要以上に長く保持されるため、最適ではありません。ビューがRuntimeExceptionをスローするとどうなるか想像してみてください。UIレンダリングが失敗したため、トランザクションはロールバックされますか?
Vlad Mihalcea

細かい回答ありがとうございました!春のブートユーザーはjpaをそのように使用しない可能性が高いため、ガイドを最後に変更するだけです。
10:24

24
  • トランザクションはサービス層でコミットできます-トランザクションはOSIVとは関係ありません。それはだSession実行している-滞在は、トランザクションを開いていないこと。

  • アプリケーションレイヤーが複数のマシンに分散している場合、OSIVを使用することはほとんどできません。オブジェクトをネットワーク経由で送信する前に、必要なすべてのものを初期化する必要があります。

  • OSIVは、遅延読み込みのパフォーマンス上の利点を活用するための優れた透過的な(つまり、コードが発生しないことを認識している)方法です。


2
最初の箇条書きに関しては、これは少なくともJBoss wikiの元のOSIVには当てはまりません。また、リクエストに関するトランザクション境界も処理します。
Pascal Thivent、2009

@PascalThiventどの部分がそう思いましたか?
イ・サンヒョン2014

13

Open Session In Viewが悪い習慣と見なされているとは言えません。何があなたにその印象を与えますか?

Open-Session-In-Viewは、Hibernateでセッションを処理するためのシンプルなアプローチです。単純なので、単純化することもあります。リクエストに複数のトランザクションを含めるなど、トランザクションをきめ細かく制御する必要がある場合、Open-Session-In-Viewが常に適切なアプローチであるとは限りません。

他の人が指摘したように、OSIVにはいくつかのトレードオフがあります-キックオフしているトランザクションを理解する可能性が低いため、N + 1の問題が発生しやすくなります。同時に、ビューの小さな変更に対応するためにサービスレイヤーを変更する必要がないことを意味します。


5

SpringなどのInversion of Control(IoC)コンテナーを使用している場合は、Beanのスコープを確認することをお勧めします。基本的に、SpringにSessionライフサイクルがリクエスト全体に及ぶHibernate オブジェクトを与えるように指示しています(つまり、HTTPリクエストの開始と終了で作成および破棄されます)。LazyLoadExceptionIoCコンテナーがセッションを管理してくれるので、セッションについて心配する必要も、セッションを閉じる必要もありません。

前述のように、N + 1 SELECTのパフォーマンスの問題について考慮する必要があります。後からいつでもHibernateエンティティを構成して、パフォーマンスが問題となる場所で熱心な結合ロードを実行できます。

BeanスコープソリューションはSpring固有ではありません。私はPicoContainerが同じ機能を提供していることを知っており、他の成熟したIoCコンテナーが同様の機能を提供していると確信しています。


1
リクエストスコープBeanを介してビューで利用できるようになっているHibernateセッションの実際の実装へのポインタはありますか?
Marvo、2012年

4

私自身の経験では、OSIVはそれほど悪くありません。私が行った唯一の配置は、2つの異なるトランザクションを使用することです。1つ目は「サービスレイヤー」で開かれ、「ビジネスロジック」があります。2つ目は、ビューのレンダリングの直前に開かれます。


3

私はブログでビューのオープンセッションをいつ使用するかに関するいくつかのガイドラインについて投稿しました。興味のある方はチェックしてみてください。

http://heapdump.wordpress.com/2010/04/04/should-i-use-open-session-in-view/


1
一般的なSOの経験則として、答えを提供する場合は、単に他の場所にリンクするだけではありません。おそらく、1つまたは2つの文または要点を示すリストされた項目を提供します。リンクしても問題ありませんが、もう少し付加価値を提供したいと考えています。それ以外の場合は、コメントを付けてそこにリンクを配置するだけです。
DWright

この回答のリンクは一読の価値があります。OSIVを使用するタイミングではなく、適切なガイダンスを提供します
ams

1

私はHibernateに対して錆びています。しかし、1つのHibernateセッションで複数のトランザクションを実行することは可能だと思います。したがって、トランザクション境界はセッションの開始/停止イベントと同じである必要はありません。

OSIV、imoは、リクエストがDBアクセスを行う必要があるたびに「持続性コンテキスト」(別名セッション)を開始するためのコードの記述を回避できるため、主に役立ちます。

サービス層では、「必須、新規必須など」など、トランザクションのニーズが異なるメソッドを呼び出す必要があるでしょう。これらのメソッドで必要なのは、誰か(つまり、OSIVフィルター)が永続化コンテキストを起動したことだけなので、心配する必要があるのは、「このスレッドの休止状態セッションを提供してください。 DBスタッフ」。


1

これはあまり役に立ちませんが、ここで私のトピックを確認できます。* OpenSessionInViewを使用したHibernate Cache1 OutOfMemory

OpenSessionInViewと多くのエンティティが読み込まれるため、OutOfMemoryの問題がいくつかあります。それらはHibernateキャッシュレベル1にとどまり、ガベージコレクションされないためです(ページごとに500アイテムのエンティティを大量に読み込みますが、すべてのエンティティはキャッシュに残ります)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.