私は、遅延初期化を使用したい顧客向けのプロジェクトに取り組んでいます。デフォルトの遅延読み込みモードでクラスをマッピングすると、常に「遅延初期化例外」が発生します。
@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クラスを使用する標準パターンはありますか?
スニペットは大歓迎です。お時間をありがとうございました。
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を心配する必要がなくなりました。
プロパティをプリフェッチするには多くの方法がありますので、セッションが閉じられた後にそれらは存在します:
JOIN FETCH
キーワードを探します。これらのソリューションを試すと、いくつかの問題が発生する場合があります。
JOIN FETCH
にしようとしているエンティティは、リストに関係する複数の「多くの」関係を介してリンクされている場合があります。この場合、結果のクエリはあいまいな結果を返し、Hibernateは単一のクエリでデータをフェッチすることを拒否します。最善の方法は、最初にJOIN FETCH
を試すことです。それでもうまくいかない場合は、ゲッターアプローチを試してください。実行時にJITコンパイラによって混乱する場合は、結果をpublic static volatile Object
に割り当てます。
または、Hibernateの使用を停止します...
接続がリークするため、Hibernate 4.1.7より前ではhibernate.enable_lazy_load_no_trans
を使用しないでください。 https://hibernate.onjira.com/browse/HHH-7524 を参照してください
LazyInitializationExceptionは、休止状態のセッションが閉じられた後、またはセッションからオブジェクトが切り離された後にコレクションを呼び出すことを意味します。
オブジェクトを休止状態セッションに再アタッチするか、コレクションを呼び出す場所を変更するか、セッションが閉じられる場所の境界を上位レイヤーに移動する必要があります。
OpenSessionInViewは、この問題に対処するための1つのパターンです。ここにいくつかの情報:
http://www.hibernate.org/43.html
このパターンを実装するときは慎重になり、その意味を理解する必要があります。ビューで遅延関連付けをナビゲートするたびに、データをロードするための別のSQLクエリが起動されます。これらのSQLクエリの数とサイズが小さいようなユースケースの場合、これは重要ではありません。データをロードするために、Hibernateがバックグラウンドで「魔法のように」実行しているクエリの種類を確認できるように、少なくともロギング設定を調整してください。
また、作成しているアプリケーションの種類も考慮してください。リモーティングを処理していない場合(Webサービスなし、AJAXベースのWebクライアントなし)、OSIVは非常にうまく機能する可能性があります。ただし、リモーティングシリアライザーがオブジェクトグラフ全体を調べ始めると、とんでもない数のSQLクエリがトリガーされ、DBとアプリサーバーに障害が発生する可能性があります。
LazyInitializationExceptionを解決する最良の方法 は、エンティティクエリでJOIN FETCHディレクティブを使用することです。
FetchType.EAGER loading はパフォーマンスに悪影響を及ぼします。また、次のようなアンチパターンがあります。
UIレンダリングのためにデータベース接続を開く必要がある(ビューでセッションを開く)か、初期永続コンテキストの外部でフェッチされるすべての遅延アソシエーションにデータベース接続が必要なため(hibernate.enable_lazy_load_no_trans
)。
エンティティが不要な場合もあり、DTOプロジェクションがさらに優れています。エンティティを変更する必要がある場合にのみ、エンティティをフェッチする必要があります。読み取り専用トランザクションの場合、- DTO予測の方が良い 。
コレクションを使用していて、遅延ロードで初期化する場合、セッションを閉じる前にそのコレクションを使用します。使用したい場合、その後セッションが閉じると、lazyはデフォルトで試行されるため、lazyinitializeExceptionが発生します。
Oracle Javaチュートリアルは、「エンタープライズBeanがトランザクション、共有オブジェクトの同時アクセスを管理するメカニズムをサポートする」と指摘しています。したがって、Lazy Fetchの問題を処理するために、ステートレスJava Session Beanを作成し、メソッドから戻る前に必要なすべてのサブクラスを取得します。これにより、遅延フェッチ例外が回避されます。 Oracleは、これを「セッションファサード」コアJ2EEパターンとも呼んでいます。このパターンは、言及されている他のプラクティスのいくつかよりも優れているようです。
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 を参照してください。