そして、LazyLoadExceptionsを回避するためにどのような代替戦略を使用しますか?
表示中の開いているセッションに次の問題があることを理解しています:
- 異なるjvmで実行されている階層化アプリケーション
- トランザクションは最後にのみコミットされ、おそらく前に結果が欲しいでしょう。
しかし、アプリケーションが単一のvmで実行されていることがわかっている場合は、オープンセッションインビュー戦略を使用して痛みを和らげてみませんか?
そして、LazyLoadExceptionsを回避するためにどのような代替戦略を使用しますか?
表示中の開いているセッションに次の問題があることを理解しています:
しかし、アプリケーションが単一のvmで実行されていることがわかっている場合は、オープンセッションインビュー戦略を使用して痛みを和らげてみませんか?
回答:
おそらく初期化されていないプロキシ、特にコレクションをビューレイヤーで送信し、そこから休止状態の読み込みをトリガーすると、パフォーマンスと理解の両方の観点から問題が発生する可能性があるためです。
理解:
OSIVを使用すると、データアクセスレイヤーに関連する問題でビューレイヤーが「汚染」されます。
ビューレイヤーはHibernateException
遅延読み込み時に発生する可能性のあるを処理する準備ができていませんが、おそらくデータアクセスレイヤーはそうです。
パフォーマンス:
OSIVはカーペットの下で適切なエンティティの読み込みを引っ張る傾向があります-コレクションまたはエンティティが遅延して初期化されている(おそらくN + 1)ことに気付かない傾向があります。利便性が高く、コントロールが少ない。
更新:この主題に関するより大きな議論については、OpenSessionInViewアンチパターンを参照してください。著者は3つの重要なポイントをリストします。
- 各遅延初期化はクエリを取得します。これは、各エンティティがN + 1クエリを必要とすることを意味します。ここで、Nは遅延関連付けの数です。画面に表形式のデータが表示されている場合、Hibernateのログを読むことは、本来行うべきではない大きなヒントです
- プレゼンテーションレイヤーでDBを使用して爪を汚すため、これはレイヤードアーキテクチャを完全に無効にします。これは概念的な詐欺なので、私はそれと共存できますが、当然の結果があります
- 最後に重要なことですが、セッションのフェッチ中に例外が発生すると、ページの書き込み中に例外が発生します。ユーザーにクリーンなエラーページを提示することはできず、実行できるのは、本文にエラーメッセージを書き込むことだけです。
より詳しい説明については、私の「Open Session In View Anti-Pattern」の記事をご覧ください。それ以外の場合、Open Session In Viewを使用してはならない理由の概要を次に示します。
Open Session In Viewは、データのフェッチに悪いアプローチをとっています。ビジネスレイヤーに、ビューレイヤーに必要なすべての関連付けを取得するのに最適な方法を決定させる代わりに、永続化コンテキストを開いたままにして、ビューレイヤーがプロキシの初期化をトリガーできるようにします。
OpenSessionInViewFilter
呼び出してopenSession
根底にあるの方法SessionFactory
と新しいを取得Session
。Session
にバインドされていますTransactionSynchronizationManager
。OpenSessionInViewFilter
呼び出しdoFilter
のjavax.servlet.FilterChain
オブジェクト参照を、要求がさらに処理されますDispatcherServlet
根本的にHTTPリクエストと呼ばれ、ルートされますPostController
。PostController
呼び出してエンティティのPostService
リストを取得しますPost
。PostService
新しいトランザクションを開き、HibernateTransactionManager
同じ再利用Session
によって開かれたものOpenSessionInViewFilter
。PostDAO
にPost
エンティティのリストをフェッチします。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
、正しい方法で処理できるようになります。
トランザクションはサービス層でコミットできます-トランザクションはOSIVとは関係ありません。それはだSession
実行している-滞在は、トランザクションを開いていないこと。
アプリケーションレイヤーが複数のマシンに分散している場合、OSIVを使用することはほとんどできません。オブジェクトをネットワーク経由で送信する前に、必要なすべてのものを初期化する必要があります。
OSIVは、遅延読み込みのパフォーマンス上の利点を活用するための優れた透過的な(つまり、コードが発生しないことを認識している)方法です。
Open Session In Viewが悪い習慣と見なされているとは言えません。何があなたにその印象を与えますか?
Open-Session-In-Viewは、Hibernateでセッションを処理するためのシンプルなアプローチです。単純なので、単純化することもあります。リクエストに複数のトランザクションを含めるなど、トランザクションをきめ細かく制御する必要がある場合、Open-Session-In-Viewが常に適切なアプローチであるとは限りません。
他の人が指摘したように、OSIVにはいくつかのトレードオフがあります-キックオフしているトランザクションを理解する可能性が低いため、N + 1の問題が発生しやすくなります。同時に、ビューの小さな変更に対応するためにサービスレイヤーを変更する必要がないことを意味します。
SpringなどのInversion of Control(IoC)コンテナーを使用している場合は、Beanのスコープを確認することをお勧めします。基本的に、SpringにSession
ライフサイクルがリクエスト全体に及ぶHibernate オブジェクトを与えるように指示しています(つまり、HTTPリクエストの開始と終了で作成および破棄されます)。LazyLoadException
IoCコンテナーがセッションを管理してくれるので、セッションについて心配する必要も、セッションを閉じる必要もありません。
前述のように、N + 1 SELECTのパフォーマンスの問題について考慮する必要があります。後からいつでもHibernateエンティティを構成して、パフォーマンスが問題となる場所で熱心な結合ロードを実行できます。
BeanスコープソリューションはSpring固有ではありません。私はPicoContainerが同じ機能を提供していることを知っており、他の成熟したIoCコンテナーが同様の機能を提供していると確信しています。
私はブログでビューのオープンセッションをいつ使用するかに関するいくつかのガイドラインについて投稿しました。興味のある方はチェックしてみてください。
http://heapdump.wordpress.com/2010/04/04/should-i-use-open-session-in-view/
私はHibernateに対して錆びています。しかし、1つのHibernateセッションで複数のトランザクションを実行することは可能だと思います。したがって、トランザクション境界はセッションの開始/停止イベントと同じである必要はありません。
OSIV、imoは、リクエストがDBアクセスを行う必要があるたびに「持続性コンテキスト」(別名セッション)を開始するためのコードの記述を回避できるため、主に役立ちます。
サービス層では、「必須、新規必須など」など、トランザクションのニーズが異なるメソッドを呼び出す必要があるでしょう。これらのメソッドで必要なのは、誰か(つまり、OSIVフィルター)が永続化コンテキストを起動したことだけなので、心配する必要があるのは、「このスレッドの休止状態セッションを提供してください。 DBスタッフ」。
これはあまり役に立ちませんが、ここで私のトピックを確認できます。* OpenSessionInViewを使用したHibernate Cache1 OutOfMemory
OpenSessionInViewと多くのエンティティが読み込まれるため、OutOfMemoryの問題がいくつかあります。それらはHibernateキャッシュレベル1にとどまり、ガベージコレクションされないためです(ページごとに500アイテムのエンティティを大量に読み込みますが、すべてのエンティティはキャッシュに残ります)。