概要
WAS(Liberty)-Db2 のJDBCアプリにおいて、
- 【1】分離レベル設定が効く優先度が下図の ⑤ > ④ > ③ > ② > ① であること
- 【2】XA(JTA)グローバルトランザクションで ④ を使ってみると何が起きるか?
を確認したログです。
WAS-Db2構成における分離レベルの設定箇所
Db2サーバーに接続するクライアントがWAS(Liberty, tWAS)である場合、分離レベルの設定箇所は以下の図の通り5か所あります。
ただし下図①と②はWASデータソースの分離レベル設定により上書きされるため、最終的な有効値としては使われません。
また、下図④は技術的には設定可能であるものの考慮点があり、WASの開発元が概ね推奨していない様子(参考Link)です。
こういった複数個所での設定が可能なパラメーターは、基本的に、適用単位がより細かい設定が有効になることが多いです。
分離レベルも以下の順に(上から)優先されます。
| 設定箇所 | 適用単位 | 特徴・考慮点 |
|---|---|---|
| ⑤ SQL WITH節 | ステートメント | 最優先される |
| ④ setTransactionIsolation() | コネクション | プールへの返却時等にWASによりリセットされる。XA利用時のリセットのタイミングに注意 |
| ③ DataSourceプロパティー [isolationLevel] |
コネクション(プール) | WAS-Db2接続時のデフォルト *デフォルト値: TRANSACTION_REPEATABLE_READ |
| ② JDBCドライバープロパティー [defaultIsolationLevel] |
コネクション | Db2 JDBC接続時のデフォルト *デフォルト値: TRANSACTION_READ_COMMITTED |
| ① Db2サーバー | サーバー/DBの既定 | 接続元(クライアント)側で指定される分離レベルにより、結果として影響しない場合がある |
分離レベルの名称
ANSI/ISOトランザクション分離レベルの名称と、Db2 for LUWにおける分離レベルの名称は異なります。
JDBCでは前者の名称が採用され、WASではJDBC APIの用語がそのまま用いられます。
更に、JDBC API(Connection.getTransactionIsolation()メソッド)により分離レベル設定値を確認することができますが、戻り値は名称ではなく数値で取得されます。
| Db2分離レベル | JDBC分離レベル ※ 括弧内:getTransactionIsolation()の戻り値 |
|---|---|
| Uncommitted Read (UR) | Connection.TRANSACTION_READ_UNCOMMITTED(1) |
| Cursor Stability (CS) | Connection.TRANSACTION_READ_COMMITTED(2) |
| Read Stability (RS) | Connection.TRANSACTION_REPEATABLE_READ(4) |
| Repeatable Read (RR) | Connection.TRANSACTION_SERIALIZABLE(8) |
"Repeatable Read"の指す分離レベルがDb2とJDBC Specとで異なるのが紛らわしいです。
参考:
Db2マニュアル「Isolation levels」
Db2マニュアル「IBM Data Server Driver for JDBC and SQLJ の分離レベル」
JavaSDK 21 JavaDoc(定数)
環境
| SW | バージョン |
|---|---|
| Java Runtime Environment (IBM Semeru) | 21.0.4 |
| Open Liberty | 25.0.0.12 |
| Db2 JCC(JDBC) Driver | 4.36.6 (*Db2 12.1.3相当) |
| Db2 for LUW | 12.1.0.3 |
- Libertyのデータソースタイプは指定していない(→XADataSource)
- @Resourceアノテーションのshareable属性は指定していない(→Shareable)
分離レベルの確認方法
方法1: JDBC APIによる取得
接続オブジェクトに設定される分離レベルは、テストコード内でConnection.getTransactionIsolation()メソッドを実行することにより確認可能です。
System.out.println(con.getTransactionIsolation());
※補足:⑤(SQL WITH節による分離レベル指定)はコネクション設定とは独立して有効になるため、JDBC APIで確認できません
方法2: Db2 MON_GET_PKG_CACHE_STMT表関数
実行SQL内のWITH節で設定される分離レベルは、方法1では確認することができません。
SQL実行後、下記SQLを実行し、Db2 モニター・エレメント EFFECTIVE_ISOLATION の値を取得します。
EFFECTIVE_ISOLATION は、ステートメントの実行中に使用された分離レベルを出力するエレメントです。
SELECT
INSERT_TIMESTAMP,
SECTION_TYPE,
PACKAGE_SCHEMA,
PACKAGE_NAME,
EFFECTIVE_ISOLATION,
SUBSTR(STMT_TEXT, 1, 100) AS STMT_TEXT
FROM
TABLE(MON_GET_PKG_CACHE_STMT(NULL, NULL, NULL, -2)) AS T1
WHERE
STMT_TEXT LIKE '%ISOTEST-%'
AND STMT_TEXT NOT LIKE 'SELECT SECTION%'
ORDER BY
INSERT_TIMESTAMP;
確認結果
検証時に設定した分離レベル
| 設定箇所 | 分離レベル設定 |
|---|---|
| ①Db2サーバー | CS: Cursor Stability(デフォルト、WASでは実質影響しない) ※Read Committed相当 |
| ②JDBC Driver | Read Committed(デフォルト) |
| ③WASデータソース | Repeatable Read(デフォルト) |
| ④setTransactionIsolation | 1本目の接続では Read Uncommitted (→SQL実行完了後、con.close()で接続をクローズ) 2本目の接続ではSerializable |
| ⑤SQLでの直接指定 | 2本目の接続上で実行されるSQL末尾でWITH URを指定 |
ローカルトランザクション(非XA)の場合の確認結果
グローバルトランザクション(XA)の場合の確認結果
確認できたこと
-
1: WAS/Libertyデフォルト(isolationLevel未指定)では、WAS→Db2接続の分離レベルはRead Stability (RS) 相当となる
- Libertyのデータソースのデフォルト設定が “JDBC Repeatable Read(4)”(JDBC/ISOの表現)であるため
- Db2側の有効分離レベル(MON_GET_PKG_CACHE_STMT.EFFECTIVE_ISOLATION)では RS(Db2の分離レベル表記)として観測される
- Db2サーバーやDb2 JDBC Driver側のデフォルトは上書きされ、WAS経由の接続では実質的に影響しない
- WAS→Db2接続の分離レベルを一律に設定変更したい場合、Libertyデータソース設定を変更する
-
2: WAS-Db2接続の分離レベルが複数箇所に設定されている場合、 ⑤ > ④ > ③ > ② > ① の順に有効になる
- ⑤WITH節による分離レベル指定
- ④Connection.setTransactionIsolation()メソッドによる分離レベル指定
- ③WASデータソースレベルで設定される分離レベル指定
- ②Db2 JCC(JDBC)ドライバーの分離レベル指定
- ①Db2 for LUWサーバーのデフォルトの分離レベル
-
3: JDBC API(*)によってWASデータソースで設定された分離レベルを上書きすることも、"技術的には可能"(考慮点あり)
- (*): Connection.setTransactionIsolation()メソッド
- setTransactionIsolation()により上書きした分離レベルは、WAS接続プールに戻されたタイミングでリセットされ、WASデータソース設定の分離レベルに戻る
- XA(JTA)のグローバルトランザクション利用時には要注意
- コミットにより、③データソースレベルの分離レベルに戻る
- "技術的には可能"な方法であるが、クライアントがWASである場合、開発者の想定した通りの動作とできるか制御が難しいため、他の方法が選択できるならば、他の方法で設定するほうが望ましい
- 参考:Transaction Isolation Levels and WebSphere Application Server
結論・感想
- 同一アプリ内で分離レベルを分けたい場合にはデータソースを分ける設計が最もシンプルでわかりやすい
- 一部、異なる分離レベルに変更したい要件がある場合にはSQL WITH節を利用可能
- なぜWAS/Liberty環境では setTransactionIsolation が推奨されにくいのか
- WAS/Libertyは 接続プールを使うため、コネクションに直接設定する方式は「意図せず別処理に影響」しやすい
- close() によりプール返却される際、WASがデータソース設定値の分離レベルに戻す(リセットする)
- JTA(XA)管理下では、commit/rollbackなどのトランザクション境界により、アプリが意図したタイミングで分離レベルが維持されない可能性がある
- 結果として「一見動くが、障害調査や性能問題が起きたときに説明不能」になりやすい
- 部分的/限定的に使う分には良いではないか、という考え方もありますが、一か所でも利用すると後にルールが徹底されづらくなるため、変更したい場合はSQL WITH節で変更するようルール統一するほうが未知の事態は防がれやすいのでは...
- 今回の発見以外にもnon-XA環境との違いがある可能性があるため、XA利用有無での動作や相違点について確認した上、方針を決定するのが安全
検証時の出力結果
後日自分で見返したくなったときのために添付。
(1) ローカルトランザクション
テストアプリ(Java)の標準出力
[INFO] 入力されたSQL Statement:[select c1,c2 from T1]
[INFO] DataSource:[com.ibm.ws.rsadapter.jdbc.v43.WSJdbc43DataSource@4d036403]
[INFO] ---- Connection Object:[com.ibm.ws.rsadapter.jdbc.v43.WSJdbc43Connection@a44909f3] ----
[INFO] ** Isolation level BEFORE setTransactionIsolation():[4]
[INFO] ** Isolation level AFTER setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED):[1]
[INFO] 1st: SQLStatement:[select c1,c2 from T1]
[INFO] ** Isolation level AFTER con.commit()):[1]
[INFO] ###### 一度接続をcloseした後、2回目、同じSQL実行#####
[INFO] ---- Connection Object:[com.ibm.ws.rsadapter.jdbc.v43.WSJdbc43Connection@244096dc] ----
[INFO] ** 2nd: Isolation level BEFORE setTransactionIsolation():[4]
[INFO] ** 2nd: Isolation level AFTER setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE):[8]
[INFO] 2nd: SQLStatement:[select c1,c2 from T1]
[INFO] ** 2nd: Isolation level AFTER con.commit()):[8]
[INFO] ###### Serializableに設定変更した接続オブジェクトは継続利用#####
[INFO] ---- Connection Object:[com.ibm.ws.rsadapter.jdbc.v43.WSJdbc43Connection@244096dc] ----
[INFO] ** 3rd: Isolation level before executing SQL with implicit ISOLATION LEVEL:[8]
[INFO] 3rd: SQLStatement:[select c1,c2 from T1 with UR /*ISOTEST-3rd-time*/]
[INFO] ** 3rd: Isolation level AFTER con.commit()):[8]
Db2 MON_GET_PKG_CACHE_STMT表関数出力
$ db2 "SELECT INSERT_TIMESTAMP, SECTION_TYPE, PACKAGE_SCHEMA, PACKAGE_NAME, EFFECTIVE_ISOLATION, SUBSTR(STMT_TEXT,1,100) as STMT_TEXT FROM TABLE(MON_GET_PKG_CACHE_STMT(NULL,NULL,NULL,-2)) as T1 WHERE STMT_TEXT LIKE '%ISOTEST-%' and STMT_TEXT NOT LIKE 'SELECT SECTION%' order by INSERT_TIMESTAMP"
INSERT_TIMESTAMP SECTION_TYPE PACKAGE_SCHEMA PACKAGE_NAME EFFECTIVE_ISOLATION STMT_TEXT
-------------------------- ------------ -------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------- ------------------- ----------------------------------------------------------------------------------------------------
2025-12-25-11.30.56.475145 D - - CS SELECT INSERT_TIMESTAMP, SECTION_TYPE, PACKAGE_SCHEMA, PACKAGE_NAME, EFFECTIVE_ISOLATION, SUBSTR(STM
2025-12-25-11.35.37.764892 D - - UR select c1,c2 from T1/*ISOTEST-1st-time*/
2025-12-25-11.35.37.845688 D - - RR select c1,c2 from T1/*ISOTEST-2nd-time*/
2025-12-25-11.35.37.922042 D - - UR select c1,c2 from T1 with UR /*ISOTEST-3rd-time*/
4 レコードが選択されました。
(2)XA(JTA)のグローバルトランザクション
テストアプリ(Java)の標準出力
[INFO] 入力されたSQL Statement:[select c1,c2 from T1]
[INFO] UserTransaction+[com.ibm.ws.transaction.services.UserTransactionService@9614a205]
[INFO] DataSource:[com.ibm.ws.rsadapter.jdbc.WSJdbcDataSource@402b84d]
[INFO] ---- Connection Object:[com.ibm.ws.rsadapter.jdbc.v41.WSJdbc41Connection@a377247b] ----
[INFO] ** Isolation level BEFORE setTransactionIsolation():[4]
[INFO] ** Isolation level AFTER setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED):[1]
[INFO] 1st: SQLStatement:[select c1,c2 from T1]
[INFO] ###### 一度接続をcloseした後、2回目、同じSQL実行#####
[INFO] ---- Connection Object:[com.ibm.ws.rsadapter.jdbc.v41.WSJdbc41Connection@620151dd] ----
[INFO] ** 2nd: Isolation level BEFORE setTransactionIsolation():[4]
[INFO] ** 2nd: Isolation level AFTER setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE):[8]
[INFO] 2nd: SQLStatement:[select c1,c2 from T1]
[INFO] ###### Serializableに設定変更した接続オブジェクトは継続利用#####
[INFO] ---- Connection Object:[com.ibm.ws.rsadapter.jdbc.v41.WSJdbc41Connection@620151dd] ----
[INFO] ** 3rd: Isolation level before executing SQL with implicit ISOLATION LEVEL:[8]
[INFO] 3rd: SQLStatement:[select c1,c2 from T1 with UR /*ISOTEST-3rd-time*/]
[INFO] ** 3rd: Isolation level AFTER UserTransaction.commit()):[4]
Db2 MON_GET_PKG_CACHE_STMT表関数出力
$ date ; db2 "SELECT INSERT_TIMESTAMP, SECTION_TYPE, PACKAGE_SCHEMA, PACKAGE_NAME, EFFECTIVE_ISOLATION, SUBSTR(STMT_TEXT,1,100) as STMT_TEXT FROM TABLE(MON_GET_PKG_CACHE_STMT(NULL,NULL,NULL,-2)) as T1 WHERE STMT_TEXT LIKE '%ISOTEST-%' and STMT_TEXT NOT LIKE 'SELECT SECTION%' order by INSERT_TIMESTAMP"
Mon Jan 5 19:42:37 JST 2026
INSERT_TIMESTAMP SECTION_TYPE PACKAGE_SCHEMA PACKAGE_NAME EFFECTIVE_ISOLATION STMT_TEXT
-------------------------- ------------ -------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------- ------------------- ----------------------------------------------------------------------------------------------------
2026-01-05-19.42.25.013239 D - - CS SELECT INSERT_TIMESTAMP, SECTION_TYPE, PACKAGE_SCHEMA, PACKAGE_NAME, EFFECTIVE_ISOLATION, SUBSTR(STM
2026-01-05-19.42.34.337487 D - - UR select c1,c2 from T1/*ISOTEST-1st-time*/
2026-01-05-19.42.34.373882 D - - RR select c1,c2 from T1/*ISOTEST-2nd-time*/
2026-01-05-19.42.34.407898 D - - UR select c1,c2 from T1 with UR /*ISOTEST-3rd-time*/
4 レコードが選択されました。
-----------------------------------------------------------------
※本記事は個人環境での検証結果であり、所属組織の公式見解ではありません。
-----------------------------------------------------------------



