web-dev-qa-db-ja.com

hibernate.enable_lazy_load_no_transでHibernate Lazy-Initの問題を解決します

私は悪名高い冬眠例外に苦しんでいます

org.hibernate.LazyInitializationException: could not initialize proxy - no Session

今、コミュニティは応援しています

<property name="hibernate.enable_lazy_load_no_trans" value="true"/>

それは問題を解決するが、注意して使用すると言います。

慎重に使用することの意味は何ですか?このプロパティは実際に何をしますか?

洞察をお願いします。前もって感謝します。

51
Sachin Verma

このアプローチの問題は、N + 1効果が得られることです。

次のエンティティがあると想像してください。

_public class Person{
    @OneToMany // default to lazy
    private List<Order> orderList;
}
_

10K人を返すレポートがあり、このレポートでperson.getOrderList()コードを実行すると、JPA/Hibernateは10Kのクエリを実行します。これはN + 1効果であり、実行されるすべてのクエリを制御することはできません。

順序が次のようになったと想像してください。

_public class Order{
    @OneToMany // default to lazy
    private List<EmailSent> emailSentList;
}
_

person.getOrderList()を繰り返し、_Order order_ごとにorder.getEmailSentList()を実行することを想像してください。今問題を見ることができますか?

LazyInitializationExceptionには、いくつかの解決策があります。

  • OpenInSessionInViewアプローチを使用します。トランザクションを開いたり閉じたりするWebFilterを作成する必要があります。問題はN + 1効果です。
  • Hibernate.enable_lazy_load_no_trans構成を使用します。これは休止状態であり、必要に応じてプロジェクトを他のJPAプロバイダーに移植することはできません。また、N + 1エフェクトを使用することもできます。
  • PersistenceContext Extendedという名前のEJB機能を使用します。これにより、複数のトランザクションのコンテキストを開いたままにします。問題は次のとおりです。N+ 1の影響が発生する可能性があり、多くのサーバーメモリを使用します(エンティティは管理されたままになります)
  • クエリでFETCHを使用します。このアプローチを使用すると、_select p from Person p join fetch p.orderList_のようなJPQL/HQLを実行できます。このクエリを使用すると、データベースからリストがロードされ、N + 1効果がなくなります。問題は、ケースごとにJPQLを記述する必要があることです。

それでも問題がある場合は、次のリンクを確認してください。

41
uaiHebert

これは、セッションコンセプトでHibernateの反復可能な読み取りセマンティクスの適用を活用する方法に反します。オブジェクトが最初にロードされ、セッションの存続期間内にオブジェクトが再度参照されると、このオブジェクトがDBで変更されたかどうかのIRRESPECTIVEが同じオブジェクトに返されます。これは、hibernateによって自動的に提供される反復可能な読み取りセマンティクスです。

この設定では、この保証を提供するセッションがないため、このデータにアクセスすると、最新バージョンのデータが取得されます。

これでいいかもしれません。ただし、このオブジェクトが長期間にわたって一定の場所に保持され、データが大幅に変更されたため、遅延フェッチされたデータは、セッションが生きているときに既にロードされたデータとは大きく異なるシナリオを考えてください。これはあなたが心配する必要があるものです。

簡単に言うと、プログラムが次の影響を受けない場合は、この設定を安全に使用できます。セッション中に既にフェッチされたデータが、セッション外で遅延フェッチされるデータにどの程度古くなっているか

しかし、これ(プログラムがタイミングの問題にさらされ、ある時点で正常に動作し、別の時点で失敗する可能性がある場合)が懸念される場合は、セッション中に必要なすべてのデータをフェッチします。

10
codedabbler

LazyInitializationExceptionを解決する最良の方法 は、エンティティクエリでJOIN FETCHディレクティブを使用することです。

EAGER loading はパフォーマンスに悪影響を及ぼします。また、次のようなアンチパターンがあります。

UIレンダリングのためにデータベース接続を開く必要がある(ビューでセッションを開く)か、初期永続コンテキストの外部でフェッチされるすべての遅延アソシエーションにデータベース接続が必要なため(hibernate.enable_lazy_load_no_trans)。

場合によっては、エンティティさえ必要とせず、 DTOプロジェクションの方が優れています

4
Vlad Mihalcea

おそらく @ Transactional のような優れたソリューションがあるため、セッションの開始と終了は「セッションを開いてから、すべてをtry-catch-finallyでラップし、catchロールバックして最後に閉じる」という非常に一般的なパターンに従います。セッション。」この注釈は通常、Webアプリとサービスのリクエストレベルにあります。

または、より詳細な制御が必要な場合は、SessionFactoryを使用して手動でセッションを開くことができます。

また、他の人が述べたように、遅延読み込みは注意する必要があるものです。これは特効薬ではありませんが、非常に役立ちます。一般的に、アプリが多くの小さなリクエストを持つように設計されている場合、それは大丈夫です。

積極的な読み込みも非常に悪い場合があります。たとえば、オブジェクトモデルに多くの多対多の関係があるが、リクエストが1レベル以上の深さのデータを使用していない場合。

または、今のところすべてを忘れることができます。問題になるまで遅延読み込みを使用します。そして、もしそうなら、とにかくMybatisの方がよかったでしょう。

2
James Watkins