AS400のDB2では、通常ではトランザクションが扱えないようです。ただし、ジャーナル処理を設定すれば扱えるようになるみたいです。*1
で、今回の案件。
- ジャーナルは作成しない
- IDはテーブルを使って採番
という方針があり、それと関連してFOR UPDATEについてあれこれ調べたりしてました。
DB2で「SELECT ... FOR UPDATE」のロックを検証 - 130単位
S2JDBC DB2DialectでのforUpdate()を調べてみた - 130単位
さて、Javaには「synchronized」という修飾子があるそうです。これをメソッドの宣言に記述すれば、排他処理が行えるとのこと。
Java スレッドの排他制御 synchronizedメソッド‐ニコニコ動画(ββ) http://www.nicovideo.jp/watch/sm2346912
というわけで、S2JDBCのサービスにid採番用のメソッドを書いてみます。
- 採番用テーブルのカラムはkeyとvalueのみ
- エンティティを作成してSQL自動生成で記述
- 各サービスから扱えるようにAbstractServiceに定義
- 最初の採番時、対象キーのレコードが無くても動作可
- valueカラムの現在値=次に採番される値
こんな感じの仕様です。
public abstract class AbstractService<ENTITY> extends S2AbstractService<ENTITY> { public synchronized int generateId(String key) { int id; IdGenerator idGen = jdbcManager .from(IdGenerator.class) .where("key = ?", key) .getSingleResult(); if (idGen == null) { id = 1; idGen = new IdGenerator(); idGen.key = key; idGen.value = 2; jdbcManager.insert(idGen).execute(); } else { id = idGen.value; idGen.value = id + 1; jdbcManager.update(idGen).execute(); } return id; } }
で、synchronizedが実際に効くのかどうか試してみましたが、いまいちわかりませんでした。
- トランザクションは無しに設定
- メソッドの採番中にユーザーAのみ分岐されるThread#sleep()を仕込む
- ユーザーAにて採番実行
- ユーザーBにて採番実行
こんな手順でやってみたのですが、synchronizedがない状態でも3の時点で止まってくれました。おそらく検証方法がまずいのか、理解が足りないかのどちらかかと思われますが…。考えてもわからないので、ひとまず一区切りとしたいと思います。