10

休止状態でのダーティ ライトに問題がありました。古いテーブルに書き込んでいるかどうかを確認できるように、@version フィールドを追加しました。これは、私が今や多くの定型コードを持っていることを意味しています

try {
   tryWriteToTable();
} catch (PersistenceExcepton) {  //subclasss of OptimisiticLockException
   try {
      tryWriteToTable();
   } catch (PersistenceExcepton) {
       //dont try again - something seriously wrong
   }
}  

私はSpringを使用していますが、このパターンを定義できるものがあるかどうか疑問に思いました. 例外がある場合に繰り返すことができるもの。Spring 以外に、この醜いボイラープレート コードをすべて回避するために使用できるものはありますか。

私はこのようなものが欲しいです

@TryTwice
private void tryWriteToTable() ....


 

ありがとう

4

2 に答える 2

11

目標を達成する最善の方法は、をキャッチしOptimisticLockingExceptionて操作を再試行できるインターセプターを使用することです。

ただし、これは、最新のエンティティ スナップショットを取得し、古いバージョンのプロパティを使用せずに切り離された状態をコピーする場合にのみ機能することに注意してください。このような戦略では、更新が失われる傾向があります。

したがって、保存しようとしているエンティティ プロパティのサブセットが、他の同時トランザクションではなく、独自のプロセスによってのみ更新できる場合にのみ、再試行戦略を使用する必要があります。

作業を簡素化するために、Maven Central でも利用できるdb-utilオープンソース プロジェクトを作成しました。これは Spring AOP に基づいており@Retry、楽観的ロック例外を取得したときに再試行するサービスをマークするための注釈を提供します。

于 2014-06-27T20:37:32.533 に答える
1

このリトライ問題に既成のものはないと思います。何かをやり直そうとしたり、PersistenceException オブジェクトをキャッチしたりする代わりに、手元にある現在のタスクのより良いパターンもあります。

Spring を使用しているため、トランザクション処理にJava Transaction Service (JTA)を使用するか、JPA 互換モードで Hibernate を使用する可能性が高いです。したがって、セッションの永続コンテキストは、トランザクションのコミット後にクリアされます。これにより、エンティティが現在の (したがって任意の) セッションから切り離されます。

したがって、特定のエンティティに対して行ったすべての変更は、セッションによって管理されなくなります (エンティティがまだセッションによって管理されている場合は、手動でデタッチするために使用できます。Hibernatesession.evict(entity)のドキュメントで、デタッチされたオブジェクトについて何かを読みたいと思うかもしれません:切り離されたオブジェクト

を使用するだけで、エンティティが表すデータベース要素の現在の状態を再読み込みできるようになりましたMyEntity currentState = session.get(EntityClass.class, detachedEntity.getId());。currentState オブジェクトのプロパティを調べることで、バージョン番号を簡単にテストできます。異なる場合は、データベース内の状態が変化しているため、特別な条件をテストしてそれに応じて対処できます。

たとえば、以前に実装したものを使用します。特定のビジネス レポートを担当者に送信する電子メール システムがあります。メールの作成には 5 分以上かかります。メールの作成は、毎日の業務を中断しないように午前 0 時以降に延期されます (これらのレポートを作成するためのデータベースの使用率は膨大です)。

したがって、私たちの(テストされていない、説明のためだけの)コードは次のようになります。

session.beginTransaction();
EmailTask task = getRandomNextTask(session);
session.getTransaction().commit(); //end transaction, task is detached

prepareEmail(); //takes 5minutes or more

session.beginTransaction();
EmailTask currentState = session.get(EmailTask.class, task.getId());
if(currentState.getVersion() == task.getVersion() || currentState.hasError()) {
     currentState.markDone();
     session.getTransaction().commit();
     sendEmail();
} 
else
    trashEmail();

最初に次の電子メール タスクを取得し、電子メールの計算を開始しますが、これには時間がかかります。次に、タスクが変更されていないかどうか、または他のプロセスによって変更されているかどうかを確認します (エラーが発生したため)。その場合、タスクを完了としてマークし、実際にメールを送信します。(データベースを変更した後ではなく、実際にメールを送信する前に、システムがクラッシュしないと想定しています。

于 2013-09-29T14:47:14.787 に答える