S2JDBCの楽観ロックのあれこれ
楽観ロックについては、色々とみたので、別エントリーで書いてみます。
まずはデータを挿入します。
<Script文=今までのテストコードも同じテーブルです>
CREATE TABLE "ORDER_VOUCHER"
( "ID" NUMBER(10,0) NOT NULL ENABLE,
"ACCOUNT_ID" VARCHAR2(10),
"ACCOUNT_NAME" VARCHAR2(30),
"VERSION" NUMBER(10,0) NOT NULL ENABLE,
CONSTRAINT "ORDER_VOUCHER_PK" PRIMARY KEY ("ID") ENABLE
)
@Entity
public class OrderVoucher {
/** idプロパティ */
@Id
@Column(precision = 10, nullable = false, unique = true)
public Long id;
/** accountIdプロパティ */
@Column(length = 10, nullable = true, unique = false)
public String accountId;
/** accountNameプロパティ */
@Column(length = 30, nullable = true, unique = false)
public String accountName;
/** versionプロパティ */
@Version
@Column(precision = 10, nullable = false, unique = false)
public Long version;
}
ServiceClass
public int insertAll(List
return this.jdbcManager.insertBatch(entities).execute();
}
テストコード
public void testInsertAll() throws Exception {
List
OrderVoucher entity = new OrderVoucher();
entity.id = Long.parseLong("1");
entity.accountId = "11";
entity.accountName = "222";
entity.version = Long.parseLong("0");
entities.add(entity);
entity = new OrderVoucher();
entity.id = Long.parseLong("2");
entity.accountId = "22";
entity.accountName = "333";
entities.add(entity);
int expecteds = orderVoucherService.insertAll(entities);
assertEquals(expecteds.length, 2);
}
VersionがNotNullが入っていますので、エラーになります。
この時、最初の1件は問題ないので、1件だけ挿入されるのかなぁ、と思いましたが、きちんとロールバックされ、データベースを確認した所、1件もデータが格納されていませんでした。
テストコード
public void testInsertAll() throws Exception {
List
OrderVoucher entity = new OrderVoucher();
entity.id = Long.parseLong("1");
entity.accountId = "11";
entity.accountName = "222";
entity.version = Long.parseLong("0");
entities.add(entity);
entity = new OrderVoucher();
entity.id = Long.parseLong("2");
entity.accountId = "22";
entity.accountName = "333";
entity.version = Long.parseLong("0");
entities.add(entity);
int[] expecteds = orderVoucherService.insertAll(entities);
assertEquals(expecteds.length, 2);
}
今度はうまくいきました。が、Versionは「0」を指定していましたが、「1」になっていました。仕様ですね、出力されたSQLをみると、一目瞭然です。
では、普通にS2AbstractServiceを使って、Updateします。
テストコード
public void testUpdate() throws Exception {
OrderVoucher entity = new OrderVoucher();
entity.id = Long.parseLong("1");
entity.accountName = "8888";
int expected = orderVoucherService.update(entity);
assertEquals(expected, 1);
}
怒られました。。。
エラー内容は、これ。
[ESSR0736]エンティティ(jp.globalsystems.businessflow.entity.OrderVoucher@272961)は既に変更されています。
SQLをみると、すぐに分かります。
DEBUG 2009-01-05 21:52:09,937 [main] update ORDER_VOUCHER set ACCOUNT_ID = null, ACCOUNT_NAME = '8888', VERSION = VERSION + 1 where ID = 1 and VERSION = null
Versionを何も指定していないからですね。
では、現在のテーブルにある「1」を指定してみます。
テストコード
public void testUpdate() throws Exception {
OrderVoucher entity = new OrderVoucher();
entity.id = Long.parseLong("1");
entity.accountName = "8888";
entity.version = Long.parseLong("1");
int expected = orderVoucherService.update(entity);
assertEquals(expected, 1);
}
成功しました。Versionも「2」になっています。
ここでVersionを「3」にUpdateします。つまり、誰かが更新した、という想定で。
そして、Versionを「2」に設定し、実行してみます。
テストコード
public void testUpdate() throws Exception {
OrderVoucher entity = new OrderVoucher();
entity.id = Long.parseLong("1");
entity.accountName = "7777";
entity.version = Long.parseLong("2");
int expected = orderVoucherService.update(entity);
assertEquals(expected, 1);
}
怒られました。想定通りです。
Throwable:org.seasar.extension.jdbc.exception.SOptimisticLockException:
[ESSR0736]エンティティ(jp.globalsystems.businessflow.entity.OrderVoucher@10dc656)は既に変更されています。
DEBUG 2009-01-05 22:07:41,312 [main] update ORDER_VOUCHER set ACCOUNT_ID = null, ACCOUNT_NAME = '7777', VERSION = VERSION + 1 where ID = 1 and VERSION = 2
つまり、
バージョンチェックはする
version属性の値は更新はする
ですね。これがデフォルトになります。
includesVersionというものが、本家サイトを見ると、ありますね。
同じテストソースにincludesVersionを入れて、実行してみます。
ServiceClass
public int updateOptimistic(OrderVoucher entity) {
return this.jdbcManager.update(entity).includesVersion().execute();
}
テストコード
public void testUpdateOptimistic() throws Exception {
OrderVoucher entity = new OrderVoucher();
entity.id = Long.parseLong("1");
entity.accountName = "7777";
entity.version = Long.parseLong("2");
int expected = orderVoucherService.updateOptimistic(entity);
assertEquals(expected, 1);
}
うまく行きました。Versionは「2」になります。
つまり、
バージョンチェックはしない
version属性の値は更新はする
ですね。
更新対象がない場合、例外を出力しない方法は以下の通りです。
ServiceClass
public int updateOptimisticException(OrderVoucher entity) {
return this.jdbcManager.update(entity).suppresOptimisticLockException().execute();
}
テストコード
public void testUpdateOptimisticException() throws Exception {
OrderVoucher entity = new OrderVoucher();
entity.id = Long.parseLong("1");
entity.accountName = "4444";
entity.version = Long.parseLong("2");
int expected = orderVoucherService.updateOptimisticException(entity);
assertEquals(expected, 1);
}
戻り値が0になります=対象がない場合は。
更新対象があった場合は、件数が戻ります(上記の場合ですと、1)。
Exceptionに飛ばさないで、0件、1件以上という分岐で処理をしたい場合、使えるのではないか、と思います。
S2JDBCでここまで処理をしてもらえるので、呼出す時にはきちんと設定すべき値は設定し、実行すれば、非常に使いやすいです。