ユース ケースは、MDB によって処理された GPS 情報を含む JMS メッセージを送信するデバイスです。デバイスがデータベースに存在しない場合は、デバイスが作成/挿入され、GPS レコードが作成/挿入されます。メッセージが送信時に処理されると、すべてが正常に機能します。この問題は、同じデバイスからのメッセージが蓄積され、互いに数ミリ秒以内に処理される場合に発生します。別の JMS プロセスがエンティティをチェックして (エンティティが存在しない) エンティティを挿入しようとする (そしてエンティティが既に存在する) 間に、1 つの JMS メッセージ プロセスがエンティティを挿入するという競合状態があるようです。
ある時点でプロセスがレコードが存在しないことを検出したため、プロセスはデバイス エンティティを複数回挿入しようとしています。処理中の前のメッセージによってレコードが挿入されているため、次の例外がスローされます。IsolationLevels を SERIALIZABLE までのすべての値に変更しようとしましたが、うまくいきませんでした。どんなアイデアでも大歓迎です!WAS 7、OpenJPA 2.0.2、EJB3.0
[8/30/12 11:14:01:319 EDT] 0000002a RegisteredSyn E WTRN0074E: Exception caught
from before_completion synchronization operation:
<openjpa-2.0.2-SNAPSHOT-r422266:1295351 fatal store error>
org.apache.openjpa.persistence.EntityExistsException: The transaction has been
rolled back. See the nested exceptions for details on the errors that occurred.
Caused by: <openjpa-2.0.2-SNAPSHOT-r422266:1295351 fatal store error>
org.apache.openjpa.persistence.EntityExistsException: ORA-00001:
unique constraint (SPW_OWN.PK_DEVICE) violated
プロセスを実行する SSB スニペット:
Device device = deviceManager.findDeviceByDeviceI(deviceString);
//check to see if device entity exists
if(device == null){
Log.logDebug(this, "device " + deviceString +" is null");
device.setDeviceI(deviceString);
//tried both create and update with no luck
//deviceManager.createDevice(device);
deviceManager.updateDevice(device);
}
JPA SSB:
@Action(Action.ACTION_TYPE.UPDATE)
public String updateDevice(Device device) throws Exception {
EntityManager em = getEntityManager();
try {
device = em.merge(device);
} finally {
em.close();
}
return "";
}
いくつかのテストを行った結果、適切な回避策が見つかりましたが、まだ 100% 満足していません。その他の提案は大歓迎です。
- JMS MDB を Bean 管理のトランザクション タイプに設定し、他の後続の SSB をコンテナー モードのままにして、実際のデータ操作コンテナーをロールバック用に管理できるようにします。
- メッセージを X 回再試行して処理するように、MDB でプログラムによるしきい値を設定します。
- SSB が呼び出されたときに EJBTransactionRolledbackException (EntityExistsException が原因) をキャッチします。
- しきい値を使用してメッセージの処理を再試行します (私の場合、前のトランザクションがコミットされ、再試行によりエンティティが存在し、メッセージが正常に処理されたことが判明したため、2 回目は成功しました)
- エラー キューに送信する前にメッセージを 2 回試行して処理するように、プログラムでしきい値を設定しました。
通常、制御フローとして例外キャッチを使用するのは好きではありません。また、JMS MDB をコンテナー管理のトランザクションとして保持し、Java EE サーバーにメッセージを再処理させる方法もあると思います。