3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Spring+Domaでトランザクション分離レベルを設定する

Last updated at Posted at 2023-12-19

トランザクション分離レベルとは

データベース管理システム上でトランザクションが複数同時に行われた場合に、どれほどの一貫性、正確性で実行するかを4段階で定義したもの。

分離レベル

SERIALIZABLE(直列化可能)

最も独立性が高い分離レベル。
トランザクションを順番に実行(直列)するのと同じように他のトランザクションのデータ更新の影響を全く受けない。
アクセスが競合すると先のトランザクションの終了を待たなければならないため性能は低い。

REPEATABLE READ(読み取り対象のデータを常に読み取る)

SERIALIZABLEに次いで独立性が高い分離レベル。
他のトランザクションによるデータ更新の影響を受けない。
トランザクション実行中は再び対象データを読み取っても同じ値が返ってくる。
しかし、他のトランザクションによるレコードの追加・削除の影響は受けるため、「ファントムリード」と呼ばれる現象が生じることがある

READ COMMITTED(確定した最新データを常に読み取る)

3番目に独立性が高い分離レベル。
他のトランザクションがコミットした変更の影響を受ける。
REPEATABLE READ同様、ファントムリードが生じることがある。
何度も同じデータを読み込むと他のトランザクションによる更新で値が変わってしまう「ノンリピータブルリード」が生じることがある。

READ UNCOMMITTED(確定していないデータまで読み取る)

最も独立性が低い分離レベル。
他のトランザクションが引き起こすあらゆる更新・変更の影響を受ける。
READ COMMITTED同様、ファントムリード、ノンリピータブルリードが生じることがある。
処理途中や不完全な状態のデータを読む込む「ダーティリード」が生じることがある。
処理を妨げるロックは最小限で済むので4つの分離レベルの中で最も高速に動作する。

今回やること

Spring + Doma2を使っているアプリケーションでトランザクション分離レベルを「READ UNCOMMITTED」にする

確認方法

SELECT @@transaction_isolation;

やりかた①

1つのsqlファイルでSELECTの直前にSETコマンド実行

sample.sql
SET SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT
  @@transaction_isolation as iso
FROM
  user
WHERE
  id = /*userId*/-1
;

結果

エラーとなり実行できない

{
  "error": {
    "message": "[DOMA2009] The SQL execution is failed.\nPATH=[xxxx/yyyy/zzz/samle.sql].\nSQL=[].\nThe cause is as follows: java.sql.SQLException: Statement.executeQuery() cannot issue statements that do not produce result sets.\nThe root cause is as follows: java.sql.SQLException: Statement.executeQuery() cannot issue statements that do not produce result sets.; SQL [SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;]; Statement.executeQuery() cannot issue statements that do not produce result sets.",
    "type": "internal_server_error"
  }
}

やりかた②

DomaのSqlアノテーションでSETコマンドを実行してからSELECTする

sample.java
@Sql("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED")
@Update
int setIso();
sample.sql
SELECT
  @@transaction_isolation as iso
FROM
  user
WHERE
  id = /*userId*/-1
;

結果

①と同じくエラーとなり実行できない

{
  "error": {
    "message": "[DOMA2009] The SQL execution is failed.\nPATH=[xxxx/yyyy/zzz/samle.sql].\nSQL=[].\nThe cause is as follows: java.sql.SQLException: Statement.executeQuery() cannot issue statements that do not produce result sets.\nThe root cause is as follows: java.sql.SQLException: Statement.executeQuery() cannot issue statements that do not produce result sets.; SQL [SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;]; Statement.executeQuery() cannot issue statements that do not produce result sets.",
    "type": "internal_server_error"
  }
}

やりかた③

別sqlファイルでSELECTの前にSETコマンド実行

query.java
public Object getUser(@NonNull Integer userId) {
    int a = dao.setIso();
    return dao.selectUser(userId);
}
dao.java
@Update(sqlFile = true)
int setIso();
setIso.sql
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
selectUser.sql
SELECT
  user.*,
  @@transaction_isolation as iso
FROM
  user
WHERE
  id = /*userId*/-1
;

結果

①②と同じくエラーとなり実行できない

{
  "error": {
    "message": "[DOMA2009] The SQL execution is failed.\nPATH=[xxxx/yyyy/zzz/samle.sql].\nSQL=[].\nThe cause is as follows: java.sql.SQLException: Statement.executeQuery() cannot issue statements that do not produce result sets.\nThe root cause is as follows: java.sql.SQLException: Statement.executeQuery() cannot issue statements that do not produce result sets.; SQL [SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;]; Statement.executeQuery() cannot issue statements that do not produce result sets.",
    "type": "internal_server_error"
  }
}

やりかた④

application.ymlで設定してみる

sample.yml
spring:
  datasource:
    hikari:
      transaction-isolation: TRANSACTION_READ_UNCOMMITTED

結果

スクリーンショット 2023-12-19 18.56.10.png
READ-UNCOMMITTEDになった

やりかた⑤

Doma2のTransactionManagerを使ってみる

sample.java
TransactionManager tm = new DbConfig(URL, USERNAME, PASSWORD).getTransactionManager();
tm.requiresNew(TransactionIsolationLevel.READ_UNCOMMITTED, () -> {
    user.set(dao.selecrUser(userId));
});

結果

スクリーンショット 2023-12-19 18.28.27.png
変わっていなかった(やり方もうちょっと考えればできそう)

やりかた⑥

アノテーションで設定

sample.java
@Transactional(isolation = Isolation.READ_UNCOMMITTED)

結果

スクリーンショット 2023-12-19 18.56.10.png
READ-UNCOMMITTEDになった

注意

書き込み権限があるユーザで実施しないとSETコマンドが実行できません
※自分はこれでつまづいていました。。。

まとめ

SpringのTransactionalアノテーションを使うのが一番楽そう。
SpringのTransactionTempleteも使えるかもしれないです(今回は試していません)

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?