ここに私が当惑しているものがあります。基本的なHibernate DAO構造を実装しようとしていますが、問題があります。
重要なコードは次のとおりです。
int startingCount = sfdao.count();
sfdao.create( sf );
SecurityFiling sf2 = sfdao.read( sf.getId() );
sfdao.delete( sf );
int endingCount = sfdao.count();
assertTrue( startingCount == endingCount );
assertTrue( sf.getId().longValue() == sf2.getId().longValue() );
assertTrue( sf.getSfSubmissionType().equals( sf2.getSfSubmissionType() ) );
assertTrue( sf.getSfTransactionNumber().equals( sf2.getSfTransactionNumber() ) );
Sfの値をsf2の対応する値と比較しようとする3番目のassertTrueで失敗します。例外は次のとおりです。
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.Java:86)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.Java:140)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.Java:190)
at com.freightgate.domain.SecurityFiling_$$_javassist_7.getSfSubmissionType(SecurityFiling_$$_javassist_7.Java)
at com.freightgate.dao.SecurityFilingTest.test(SecurityFilingTest.Java:73)
at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:39)
at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:25)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.Java:40)
問題は、 detached であるオブジェクトのコレクションにアクセスしようとしていることです。コレクションに現在のセッションにアクセスする前に、オブジェクトを再アタッチする必要があります。あなたはそれをすることができます
session.update(object);
lazy=false
を使用することは、休止状態の遅延初期化機能を破棄するため、良い解決策ではありません。 lazy=false
の場合、オブジェクトが要求されると同時にコレクションがメモリにロードされます。つまり、1000個のアイテムを持つコレクションがある場合、それらにアクセスするかどうかにかかわらず、それらはすべてメモリにロードされます。そして、これは良くありません。
これをお読みください 記事 問題、可能な解決策、およびこの方法で実装されている理由を説明しています。また、セッションとトランザクションを理解するには、 この他の記事 を読む必要があります。
これは通常、所有するHibernateセッションがすでに閉じていることを意味します。次のいずれかの方法で修正できます。
HibernateTemplate.initialize(object name)
を使用してくださいlazy=false
を使用します。私の記事をご覧ください。私は同じ問題を抱えていました-LazyInitializationException-そして私がついに思いついた答えは次のとおりです:
http://community.jboss.org/wiki/LazyInitializationExceptionovercome
lazy = falseの設定は答えではありません-すべてを一度に読み込むことができますが、必ずしも良いとは限りません。例:
1つのレコードテーブルAの参照:
5つのレコードテーブルBの参照:
25レコードのテーブルCの参照:
125レコードテーブルD
...
等。これは、何がうまくいかないかのほんの一例です。
-ティム・サビン
JPAアノテーションでhibernateを使用している場合、これは便利です。サービスクラスには、@ PersistenceContextを持つエンティティマネージャーのセッターが必要です。これを@PersistenceContext(type = PersistenceContextType.EXTENDED)に変更します。その後、任意の場所で遅延プロパティにアクセスできます。
遅延読み込みを使用している場合、メソッドに注釈を付ける必要があります
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
ステートレスセッションEJBの場合
このエラーも発生しました。この問題を解決するために行ったのは、Hibernateマッピングファイルにlazy = falseを追加したことです。
別のクラスBをロードするセッション内にクラスAがあったようです。クラスBのデータにアクセスしようとしていますが、このクラスBはセッションから切り離されています。
このクラスBにアクセスするには、クラスAのHibernateマッピングファイルでlazy = false属性を指定する必要がありました。例えば、
<many-to-one name="classA"
class="classB"
lazy="false">
<column name="classb_id"
sql-type="bigint(10)"
not-null="true"/>
</many-to-one>
lazy=false
の影響について知っていて、それをデフォルトにしたい場合(たとえば、プロトタイピングの目的で)、次のいずれかを使用できます。
default-lazy="false"
を<hibernate-mapping>
要素に追加します@Proxy(lazy=false)
をエンティティクラスに追加しますDAOのみがセッションを使用しているようです。したがって、DAOメソッドの呼び出しごとに新しいセッションが開いてから閉じます。したがって、プログラムの実行は次のように再開できます。
// open a session, get the number of entity and close the session
int startingCount = sfdao.count();
// open a session, create a new entity and close the session
sfdao.create( sf );
// open a session, read an entity and close the session
SecurityFiling sf2 = sfdao.read( sf.getId() );
// open a session, delete an entity and close the session
sfdao.delete( sf );
etc...
デフォルトでは、エンティティのコレクションと関連付けは遅延型です。これらはオンデマンドでデータベースからロードされます。副<文>この[前述の事実の]結果として、それ故に、従って、だから◆【同】consequently; therefore <文>このような方法で、このようにして、こんなふうに、上に述べたように◆【同】in this manner <文>そのような程度まで<文> AひいてはB◆【用法】A and thus B <文>例えば◆【同】for example; as an example:
sf.getSfSubmissionType().equals( sf2.getSfSubmissionType() )
データベースからの新しい読み込みを要求し、エンティティの読み込みに関連付けられたセッションが既に閉じられているため、例外をスローしています。
この問題を解決するには、2つのアプローチがあります。
すべてのコードを囲むセッションを作成します。したがって、2番目のセッションを開かないようにDAOコンテンツを変更することを意味します
セッションを作成し、アサーションの前にエンティティをこのセッションに更新(再接続)します。
session.update(object);
さて、最終的に私が逃した場所を見つけました。私は、各DAOメソッドをトランザクションでラップする必要があるという誤った考えに基づいていました。ひどく間違っています!私はレッスンを学びました。すべてのDAOメソッドからすべてのトランザクションコードを収集し、アプリケーション/マネージャーレイヤーで厳密にトランザクションを設定しました。これですべての問題が完全に解決しました。データは必要に応じて適切に遅延ロードされ、コミットするとラップされて閉じられます。
人生はいいです... :)
遅延フィールドにHibernate.initializeを使用します
Hibernateセッションを手動で管理している場合は、sessionFactory.getCurrentSession()および関連ドキュメントを次の場所で確認できます。
http://www.hibernate.org/hib_docs/v3/reference/en/html/architecture-current-session.html
ピコは、彼の応答でhbmファイルがあることを意味していると思います。 Tax.Javaというファイルがあります。マッピング情報は、hbm(= hibernate mapping)ファイルに保存されます。クラスタグには、lazyというプロパティがあります。そのプロパティをtrueに設定します。次のhbmの例は、lazyプロパティをfalseに設定する方法を示しています。
`id ... '
注釈を使用している場合は、代わりに休止状態のドキュメントを参照してください。 http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/
それがお役に立てば幸いです。
SpringおよびJPAアノテーションを使用する場合、遅延初期化のセッションの問題を回避する最も簡単な方法はリプレイです:
@PersistenceContext
に
@PersistenceContext(type = PersistenceContextType.EXTENDED)
デフォルトでは、すべてのone-to-many
およびmany-to-many
アソシエーションは、初めてアクセスされたときに遅延してフェッチされます。
ユースケースでは、すべてのDAO操作を1つの論理トランザクションにラップすることにより、この問題を克服できます。
transactionTemplate.execute(new TransactionCallback<Void>() {
@Override
public Void doInTransaction(TransactionStatus transactionStatus) {
int startingCount = sfdao.count();
sfdao.create( sf );
SecurityFiling sf2 = sfdao.read( sf.getId() );
sfdao.delete( sf );
int endingCount = sfdao.count();
assertTrue( startingCount == endingCount );
assertTrue( sf.getId().longValue() == sf2.getId().longValue() );
assertTrue( sf.getSfSubmissionType().equals( sf2.getSfSubmissionType() ) );
assertTrue( sf.getSfTransactionNumber().equals( sf2.getSfTransactionNumber() ) );
return null;
}
});
別のオプションは、エンティティのロード時にすべてのLAZYアソシエーションを取得することです。
SecurityFiling sf2 = sfdao.read( sf.getId() );
lAZY submissionType
も取得する必要があります。
select sf
from SecurityFiling sf
left join fetch.sf.submissionType
この方法では、すべての遅延プロパティを積極的にフェッチし、セッションが閉じられた後にそれらのプロパティにアクセスできます。
(デカルト積を実行するため)[one|many]-to-one
アソシエーションと1つの「[one | many] -to-many」リストアソシエーションをフェッチできます。
複数の「[one | many] -to-many」を初期化するには、ルートエンティティをロードした直後に Hibernate.initialize(collection) を使用する必要があります。