「JDBCによる悲観ロックの落とし穴」の版間の差分

提供: tknotebook
移動: 案内検索
3行: 3行:
  
  
JDBCを使って悲観ロックでレコードを更新するとき、たまにこんなコードを見かけます。
+
JDBCを使って悲観ロックでレコードを更新する場合、たまにこんなコードを見かけることがあります。
  
 
  rsA = s.executeQuery("select name, balance from account where name='A' for update");
 
  rsA = s.executeQuery("select name, balance from account where name='A' for update");
16行: 16行:
 
   
 
   
 
  conn.commit();
 
  conn.commit();
 +
 +
実は Isolation level が READ COMMITED では、これは悲観ロックになっていないのです。
 +
 +
 +
コードでは、
 +
#更新モードでカーソルを取得
 +
#カーソルの行の値を取得に更新ロックをかける。
 +
#行の値を更新し排他ロックをかける。
 +
 +
問題点は更新ロックの寿命です。
 +
 +
多くのDBでは、行ロックの更新ロックは、Isolation Level が READ COMMITED の場合カーソルの指す行のみがロックされ、
 +
カーソルが別の行へ移ると元の行の更新ロックは解除されます。カーソルがクローズされれば更新ロックは当然残りません。
 +
 +
Statement オブジェクトは executeXXX を実行すると、カーソルをクローズするため、上記のコードではレコードが排他ロックするまでの
 +
わずかな隙間時間に、レコードのロックがない期間が存在するのです。これではうまく動きません。
 +
 +
対処方法は2つあります。
 +
 +
#Isolation Level を REPEATABLE READ 以上にする。
 +
#以下のように、カーソルを使って行を更新する。
 +
 +
rsA = s.executeQuery("select name, balance from account where name='A' for update");
 +
if (rsA.next()) {
 +
    balanceA = rsA.getInt("balance");
 +
    logger.debug("balanceA = " + balanceA);
 +
    rsA.updateInt("balance", balanceA - 100);
 +
    rsA.updateRow();
 +
    logger.debug("update A");
 +
} else {
 +
    throw new Exception("行がありません");
 +
  }

2016年7月30日 (土) 03:16時点における版

メインページ>コンピュータの部屋#Java>Java Tips


JDBCを使って悲観ロックでレコードを更新する場合、たまにこんなコードを見かけることがあります。

rsA = s.executeQuery("select name, balance from account where name='A' for update");
if (rsA.next()) {
    balanceA = rsA.getInt("balance");
    logger.debug("balanceA = " + balanceA);
} else {
    throw new Exception("行がありません");
}
s.executeUpdate(String.format("update account set balance=%d where name='A'", balanceA - 100));
logger.debug("update A");

conn.commit();

実は Isolation level が READ COMMITED では、これは悲観ロックになっていないのです。


コードでは、

  1. 更新モードでカーソルを取得
  2. カーソルの行の値を取得に更新ロックをかける。
  3. 行の値を更新し排他ロックをかける。

問題点は更新ロックの寿命です。

多くのDBでは、行ロックの更新ロックは、Isolation Level が READ COMMITED の場合カーソルの指す行のみがロックされ、 カーソルが別の行へ移ると元の行の更新ロックは解除されます。カーソルがクローズされれば更新ロックは当然残りません。

Statement オブジェクトは executeXXX を実行すると、カーソルをクローズするため、上記のコードではレコードが排他ロックするまでの わずかな隙間時間に、レコードのロックがない期間が存在するのです。これではうまく動きません。

対処方法は2つあります。

  1. Isolation Level を REPEATABLE READ 以上にする。
  2. 以下のように、カーソルを使って行を更新する。
rsA = s.executeQuery("select name, balance from account where name='A' for update");
if (rsA.next()) {
    balanceA = rsA.getInt("balance");
    logger.debug("balanceA = " + balanceA);
    rsA.updateInt("balance", balanceA - 100);
    rsA.updateRow();
    logger.debug("update A");
} else {
   throw new Exception("行がありません");
 }