「JDBCによる悲観ロックの落とし穴」の版間の差分
提供: tknotebook
3行: | 3行: | ||
− | + | 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 では、これは悲観ロックになっていないのです。
コードでは、
- 更新モードでカーソルを取得
- カーソルの行の値を取得に更新ロックをかける。
- 行の値を更新し排他ロックをかける。
問題点は更新ロックの寿命です。
多くの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("行がありません"); }