web-dev-qa-db-ja.com

JPAおよびHibernateを使用しているときにLazyInitializationExceptionを解決する方法

私は、遅延初期化を使用したい顧客向けのプロジェクトに取り組んでいます。デフォルトの遅延読み込みモードでクラスをマッピングすると、常に「遅延初期化例外」が発生します。

@JoinTable(name = "join_profilo_funzionalita", joinColumns = {@JoinColumn(name =    "profilo_id", referencedColumnName = "profilo_id")}, inverseJoinColumns = {@JoinColumn(name = "funzionalita_id", referencedColumnName = "funzionalita_id")})
//@ManyToMany(fetch=FetchType.EAGER) - no exceptions if uncommented
@ManyToMany 
private Collection<Funzionalita> funzionalitaIdCollection;

このエラーを回避するためにJPAクラスを使用する標準パターンはありますか?

スニペットは大歓迎です。お時間をありがとうございました。

50
rupertin

Hibernate 4.1.6はついにこの問題を解決します: https://hibernate.atlassian.net/browse/HHH-7457

Hibernate-property hibernate.enable_lazy_load_no_trans = trueを設定する必要があります

Springでの方法は次のとおりです。

<bean id="entityManagerFactory"
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="myDataSource"/>
    <property name="packagesToScan" value="com.mycompany.somepackage"/>
    <property name="jpaVendorAdapter" ref="hibernateVendorAdapter"/>
    <property name="jpaDialect" ref="jpaDialect"/>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.enable_lazy_load_no_trans">true</prop>
        </props>
    </property>
</bean>

出来上がり; hibernate-session(「JPA-speak」の永続コンテキスト)の外でドメインモデルをナビゲートしているときに、LazyInitializationExceptionを心配する必要がなくなりました。

60
andreak

プロパティをプリフェッチするには多くの方法がありますので、セッションが閉じられた後にそれらは存在します:

  1. 適切なゲッターを呼び出します。フィールドがBeanにフェッチされた後、セッションが閉じられた後にそこにあります。
  2. EJBQL queryでフィールドを初期化できます。JOIN FETCHキーワードを探します。
  3. サポートしているHibernateバージョンを使用している場合は、AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANSを有効にします。

これらのソリューションを試すと、いくつかの問題が発生する場合があります。

  1. ゲッターの呼び出しは、JITコンパイラーによって最適化される場合があります(これにはしばらく時間がかかります)。
  2. JOIN FETCHにしようとしているエンティティは、リストに関係する複数の「多くの」関係を介してリンクされている場合があります。この場合、結果のクエリはあいまいな結果を返し、Hibernateは単一のクエリでデータをフェッチすることを拒否します。
  3. AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANSに関連する興味深いバグ がすでに1つあります。そして、休止状態の人が言うように、もっとあります:注:これはトランザクションの外部で発生する可能性があり、安全ではありません。注意して使用してください。あなたはほとんど自分でしています。

最善の方法は、最初にJOIN FETCHを試すことです。それでもうまくいかない場合は、ゲッターアプローチを試してください。実行時にJITコンパイラによって混乱する場合は、結果をpublic static volatile Objectに割り当てます。

または、Hibernateの使用を停止します...

16
jb.

接続がリークするため、Hibernate 4.1.7より前ではhibernate.enable_lazy_load_no_transを使用しないでください。 https://hibernate.onjira.com/browse/HHH-7524 を参照してください

15
Hani

LazyInitializationExceptionは、休止状態のセッションが閉じられた後、またはセッションからオブジェクトが切り離された後にコレクションを呼び出すことを意味します。

オブジェクトを休止状態セッションに再アタッチするか、コレクションを呼び出す場所を変更するか、セッションが閉じられる場所の境界を上位レイヤーに移動する必要があります。

8
Daniel Alexiuc

OpenSessionInViewは、この問題に対処するための1つのパターンです。ここにいくつかの情報:

http://www.hibernate.org/43.html

このパターンを実装するときは慎重になり、その意味を理解する必要があります。ビューで遅延関連付けをナビゲートするたびに、データをロードするための別のSQLクエリが起動されます。これらのSQLクエリの数とサイズが小さいようなユースケースの場合、これは重要ではありません。データをロードするために、Hibernateがバックグラウンドで「魔法のように」実行しているクエリの種類を確認できるように、少なくともロギング設定を調整してください。

また、作成しているアプリケーションの種類も考慮してください。リモーティングを処理していない場合(Webサービスなし、AJAXベースのWebクライアントなし)、OSIVは非常にうまく機能する可能性があります。ただし、リモーティングシリアライザーがオブジェクトグラフ全体を調べ始めると、とんでもない数のSQLクエリがトリガーされ、DBとアプリサーバーに障害が発生する可能性があります。

5
cliff.meyers

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

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

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

エンティティが不要な場合もあり、DTOプロジェクションがさらに優れています。エンティティを変更する必要がある場合にのみ、エンティティをフェッチする必要があります。読み取り専用トランザクションの場合、- DTO予測の方が良い

5
Vlad Mihalcea

コレクションを使用していて、遅延ロードで初期化する場合、セッションを閉じる前にそのコレクションを使用します。使用したい場合、その後セッションが閉じると、lazyはデフォルトで試行されるため、lazyinitializeExceptionが発生します。

4
SANTOSH Kumar

Oracle Javaチュートリアルは、「エンタープライズBeanがトランザクション、共有オブジェクトの同時アクセスを管理するメカニズムをサポートする」と指摘しています。したがって、Lazy Fetchの問題を処理するために、ステートレスJava Session Beanを作成し、メソッドから戻る前に必要なすべてのサブクラスを取得します。これにより、遅延フェッチ例外が回避されます。 Oracleは、これを「セッションファサード」コアJ2EEパターンとも呼んでいます。このパターンは、言及されている他のプラクティスのいくつかよりも優れているようです。

1
K.Nicholas

ModelMapperを使用してエンティティをDTOにマッピングする際の一般的なJPA問題の解決を目的としたプロジェクトに取り組んでいます。この問題はプロジェクトですでに解決されています。プロジェクトリンク: JPA Model Mapper

「エンティティを遅延ロードとして宣言することはパフォーマンスにとって重要です。したがって、データが必要になるたびに関連するすべてのエンティティをフェッチする必要はありません。しかし、この手法はいくつかの問題につながります。最も一般的なものは、 。ほとんどの場合、アクセスされた場合に例外をスローするオブジェクトではなく、ロードされていないエンティティにnullオブジェクトが必要になります...」

ソース: JPA Model Mapper

したがって、プロジェクトでは、ロードされていないすべてのエンティティにnullを設定することにより、LazyInitializationExceptionを処理します。以下の例は、その仕組みを示しています。

読み込まれていないすべてのエンティティにnullを設定するエンティティの再マッピング:

TypedQuery<SystemEntity> query =
        em.createQuery("select s from SystemEntity s where s.id = 1",  SystemEntity.class);

SystemEntity system = query.getSingleResult();
return new JpaModelMapper(em).mapEntity(system, SystemEntity.class);

ロードされていないすべてのエンティティに対してnullを設定するDTOへのエンティティの再マッピング:

TypedQuery<SystemEntity> query =
        em.createQuery("select s from SystemEntity s where s.id = 1",  SystemEntity.class);

SystemEntity system = query.getSingleResult();
return new JpaModelMapper(em).mapEntity(system, SystemDTO.class);

詳細については、 JPA Model Mapper を参照してください。