2013/08/07

JPA DB更新 排他処理 ロック

★楽観的ロック
レコード毎にバージョン番号を持ち、まず最初に対象となるデータを取得しておき、更新時にDB上の対象データのバージョン番号に変化が無いか調べ、変化があれば「自分より前に、自分以外の誰かが更新した=このまま登録しては他人の変更を上書きしてしまうのでダメ」、変化がなければ「誰にも更新されていない=そのまま更新してよい=その一瞬だけロックして書き込んですぐに開放する」と判断するというのが楽観的ロック。CVS、SVNなどのリビジョン番号での管理がこれに当たる。

この楽観的ロックに関する機能がJPAには備わっているというので、
エンティティでバージョン番号を意味する列に「 @Version」アノテーションを付加するだけ!



@Entity
@Table(name="sample_entity")
public class SampleEntity implements Serializable {

  @Id
  @Column(unique=true, nullable=false, length=100)
  private String key;

  @Column(nullable=false, length=100)
  private String val;

  @Version
  @Column(nullable=false)
  private long revision;

  …

このようなエンティティクラスを使用して更新処理を行った場合、エラーの無い場合(新規登録時含む)はそのまま処理が進み、排他エラーになった場合はOptimisticLockExceptionが発生する。



★悲観的ロック

操作開始から終了までずっとロックしっぱなしにして、その間は他からの更新を許さないのが悲観的ロック。
ロックをかける方法は主に以下の二つ。

    検索時にロック
    任意のタイミングでロック



// 検索時にロックする場合・主キー検索
SampleEntity entity1 =
  em.find(SampleEntity.class, "abc",
          LockModeType.PESSIMISTIC_WRITE);

// 検索時にロックする場合・複数件検索
List<SampleEntity> entities =
  em.createQuery("select s from SampleEntity s")
    .setLockMode(LockModeType.PESSIMISTIC_WRITE)
    .getResultList();



// 任意のタイミングでロックする場合
SampleEntity entity2 = em.find(SampleEntity.class, "abc");
em.lock(entity2, LockModeType.PESSIMISTIC_WRITE);