環境:
JBoss 4.2.3.GA サーバーにデプロイされ、Hibernate 3.4 と JTA 1.0 を使用するアプリケーションがあります。
特定のエンティティを作成または更新してから、一部のデータをインポートするインポーターがあります。そのインポートのほとんどは、いくつかの理由により新しいトランザクションで行われ、各トランザクション内で、外部トランザクションで作成/更新されたエンティティが再度更新される可能性があります。
呼び出しシーケンスは、次の疑似コードのようになります。
サービス1:
//container managed transaction T1 is started here
import() {
A a = ... ;//new or read from database
if( isNew( a ) ) {
create(a);
} else {
update(a);
}
Service1 s = ...; //injected or looked up
for( D d : someDataList ) {
//nested transaction T2 is started due to this call, T1 should be suspended
s1.importData(d);
//nested transaction T2 should have been committed here
}
サービス 2:
@TransactionAttribute(REQUIRES_NEW)
importData(D d) {
A a = ...; //get the corresponding A instance and update as needed
update(a);
//other stuff such as importing d
}
問題:
現在の問題は、最終的にいくつかのトランザクションが同じテーブルをロックしようとする競合状態に遭遇することですが、これまでのところ、問題を再現することも、本当の原因を特定することもできませんでした.
ただし、いくつかの仮定があります。
T1 の間にいくつかのエンティティが更新されるため、トランザクションはいくつかのデータベース ロックを取得します。次に、T2 が開始されたため、T1 は中断されます。T2 は、同じデータベース ロックを取得しようとするため、ブロックされます。T2 は最終的にタイムアウトになり、T1 は正常に終了してロックを解放できます。
考えられる解決策は?:
T1 のすべての更新を別のトランザクション T1* にラップし (T1 を完全にスキップする可能性があります)、T1* と T2 を順番に実行します。
ビジネスケースでこれが許可されている場合、これは正気の解決策でしょうか(私はそのビジネスケースを自分で実装していないため、わかりません)?
他の解決策もあるかもしれません。もしそうなら、いくつかのヒントを提供してください。ただし、T2 がロックを取得しようとする前に T1 がロックを解放する必要があるように思われるため、基本的に T1 と T2 を順次実行することになるとは思えません。
質問
以上のことから、次の疑問が生じます。
- つまり、T1 は、T2 も必要とするロックを保持していて、中断されているために解放できないのでしょうか?
- 上記で説明したソリューションが唯一のアプローチですか、それとも手動のトランザクション区分を使用しない他の方法はありますか?
これをすべて読んでくれてありがとう:)
更新 1:
私はコードの作成者ではないので、コードについても掘り下げる必要があります。これまでのところ、Hibernate での明示的なロックに関するヒントはありません。したがって、AFAIK Hibernate は、データベース resp への書き込み中にのみデータベース ロックを使用します。データベース接続が開かれたとき。
自動フラッシュを使用しているため、場合によっては T2 が接続を試行する前に T1 が接続を開くことがありますが、T2 がコミットするまで接続が中断されるため、T1 は接続をコミットして閉じることができません。したがって、T1 のフラッシュのためにデータベースが取得したように見えるロックは、T2 のフラッシュ前にも解放できません。
手動フラッシュを使用しても解決策にはなりません。T2 が T1 の前にコミットした場合に更新が失われるためです。ただし、エンティティへの変更は逆になります。設計上の欠陥であることは承知しており、それを修正する必要がありますが、賢明な修正を提供するために、私たちの仮定が正しいことも確認したいと思います:)