3

私はEntityManagerAPIを調べており、レコードロックを実行する順序を理解しようとしています。基本的に、ユーザーがレコードを編集することを決定した場合、私のコードは次のとおりです。

entityManager.getTransaction().begin();
r = entityManager.find(Route.class, r.getPrimaryKey());
r.setRoute(txtRoute.getText());
entityManager.persist(r);
entityManager.getTransaction().commit();

試行錯誤の結果、 WWEntityManager.entityManager.lock(r, LockModeType.PESSIMISTIC_READ);の後に設定する必要があるよう.begin()です。

私は当然WWEntityManager.entityManager.lock(r, LockModeType.NONE);、コミット後に使用すると思いましたが、それは私にこれを与えました:

Exception Description: No transaction is currently active

私はまだコミットの前にそれを入れようとはしていませんが、50人のユーザーが一度に変更をコミットしようとした場合にレコードが衝突しないようにすることが私の目標なので、レコードをロックする目的を損なうことはありませんか?

編集中にレコードをロックする方法についてのヘルプは大歓迎です!

ありがとう!

4

2 に答える 2

1

トランザクション内でロックを実行することは完全に理にかなっています。ロックはトランザクションの終了時に自動的に解放されます(コミット/ロールバック)。ロックの解放はトランザクションの終了に関連付けられているため、(JPAのコンテキストで)トランザクションの外部でロックすることは意味がありません。また、変更が実行されてトランザクションがコミットされた後にロックしても、あまり意味がありません。

悲観的なロックを、本来の目的以外の目的で使用している可能性があります。私の仮定が間違っている場合は、答えの終わりを無視することができます。トランザクションがエンティティ(行)に対して悲観的な読み取りロックを保持している場合、次のことが保証されます。

  • ダーティリードなし:他のトランザクションは、ロックされた行に対して実行した操作の結果を確認できません。
  • 繰り返し可能な読み取り:他のトランザクションからの変更なし
  • トランザクションがロックされたエンティティを変更する場合、PESSIMISTIC_READはPESSIMISTIC_WRITEにアップグレードされます。ロックをアップグレードできない場合、トランザクションは失敗します。

以下は、トランザクションの開始時にロックを取得するシナリオを大まかに説明しています。

entityManager.getTransaction().begin();
r = entityManager.find(Route.class, r.getPrimaryKey(), 
      LockModeType.PESSIMISTIC_READ);
//from this moment on we can safely read r again expect no changes
r.setRoute(txtRoute.getText());
entityManager.persist(r);
//When changes are flushed to database, provider must convert lock to 
//PESSIMISTIC_WRITE, which can fail if concurrent update
entityManager.getTransaction().commit();

多くの場合、データベースはペシミスティック読み取りを個別にサポートしていないため、PESSIMISTIC_READ以降、実際には行へのロックを保持しています。また、PESSIMISTIC_READを使用することは、ロックされた行への変更が予期されない場合にのみ意味があります。上記の場合、変更は常に行われるため、PESSIMISTIC_WRITEを最初から使用すると、同時更新のリスクを回避できるため、合理的です。

多くの場合、悲観的なロックの代わりに楽観的なロックを使用することも理にかなっています。ロック戦略の選択に関する良い例といくつかのコメントは、JavaPersistence2.0でのロックと同時実行性から見つけることができます。

于 2012-06-01T19:26:03.510 に答える
0

変更するデータの書き込みロックを安全に行うためのすばらしい作業です。:)しかし、あなたは船外に出る/それを長い道のりでやっているかもしれません。

  • 最初にマイナーな点。persist()の呼び出しは必要ありません。更新するには、find()から返されたエンティティの属性を変更するだけです。entityManagerは変更を自動的に認識し、コミット中にそれらをデータベースに書き込みます。Persistが必要になるのは、新しいオブジェクトを作成してそれを初めてデータベースに書き込む場合(または、新しい子オブジェクトを親リレーションに追加し、cascade = PERSISTを介して永続をカスケードする場合)です。

  • ほとんどのアプリケーションでは、独自の個別のトランザクションと個別の永続コンテキストを持つ異なるスレッドによる同じデータへの同時更新が「衝突」する可能性が低くなります。これが当てはまり、スケーラビリティを最大化したい場合は、悲観的な読み取りまたは書き込みロックではなく、楽観的な書き込みロックを使用してください。これは、大多数のWebアプリケーションに当てはまります。まったく同じデータ整合性、はるかに優れたパフォーマンス/スケーラビリティを提供しますが、(まれに)OptimisticLockExceptionを処理する必要があります。

  • 楽観的な書き込みロックは、dbとエンティティにshort / integer / long / TimeStamp属性を設定し、エンティティに@Versionアノテーションを付けるだけで自動的に組み込まれます。その場合、entityManager.lock()を呼び出す必要はありません。

上記に満足し、エンティティに@Version属性を追加した場合、コードは次のようになります。

try {
   entityManager.getTransaction().begin();
   r = entityManager.find(Route.class, r.getPrimaryKey());
   r.setRoute(txtRoute.getText());
   entityManager.getTransaction().commit();
} catch (OptimisticLockException e) {
   // Logging and (maybe) some error handling here.
   // In your case you are lucky - you could simply rerun the whole method.
   // Although often automatic recovery is difficult and possibly dangerous/undesirable
   // in which case we need to report the error back to the user for manual recovery 
}

つまり、明示的なロックはまったくありません。エンティティマネージャはそれを自動的に処理します。

同時データ更新の「衝突」を回避する必要が強く、スケーラビリティが制限されたコードを使用できる場合は、悲観的な書き込みロックを介してデータアクセスをシリアル化します。

try {
   entityManager.getTransaction().begin();
   r = entityManager.find(Route.class, r.getPrimaryKey(), LockModeType.PESSIMISTIC_WRITE);
   r.setRoute(txtRoute.getText());
   entityManager.getTransaction().commit();
} catch (PessimisticLockException e) {
   // log & rethrow
}

どちらの場合も、コミットの成功または自動ロールバックの例外は、実行されたロックが自動的にクリアされることを意味します。

乾杯。

于 2012-10-13T14:49:15.230 に答える