トランザクション
概要
Komapperはトランザクション管理の高レベルAPIを提供します。
Note
KomapperをSpring FrameworkやQuarkusと組み合わせる場合、 このAPIはSpring FrameworkやQuarkusのトランザクションマネージャと連動します。トランザクションの制御
Komapperが提供するトランザクション制御のためのAPIはJDBC版とR2DBC版で異なりますが見た目上のインターフェースは統一されています。
JdbcDatabase
もしくはR2dbcDatabase
が下記のようにdb
という変数で宣言されていることを前提に説明を進めます。
val db = JdbcDatabase("jdbc:h2:mem:example;DB_CLOSE_DELAY=-1")
val db = R2dbcDatabase("r2dbc:h2:mem:///example;DB_CLOSE_DELAY=-1")
トランザクションの開始と終了
db
に定義されたwithTransaction
関数の呼び出すことでトランザクションを開始できます。
db.withTransaction {
..
}
withTransaction
関数にはトランザクション属性とトランザクションプロパティを指定できます。
db.withTransaction(
transactionAttribute = TransactionAttribute.REQUIRES_NEW,
transactionProperty = TransactionProperty.IsolationLevel.SERIALIZABLE) {
..
}
トランザクション属性は以下の2種類のいずれかを指定できます。
- REQUIRED
- 現在のトランザクションをサポートし、存在しない場合は新しいトランザクションを作成する
- REQUIRES_NEW
- 新しいトランザクションを作成し、現在のトランザクションが存在する場合はそれを一時停止する
トランザクションプロパティは、様々なプロパティ要素を含むことができます。
- IsolationLevel
- トランザクション分離レベル
- LockWaitTime
- ロック待機時間
- Name
- トランザクションの名前
- ReadOnly
- トランザクションをリードオンリーモードで開始するかどうか
複数のトランザクションプロパティ要素を +
演算子で1つのトランザクションプロパティにまとめることができます。
val property = TransactionProperty.IsolationLevel.SERIALIZABLE + TransactionProperty.Name("myTx") + TransactionProperty.ReadOnly(true)
withTransaction
関数の呼び出しが終わるとトランザクションはコミットもしくはロールバックされます。
ロールバックされる条件は次のとおりです。
withTransaction
関数の呼び出しが例外のスローにより終了するwithTransaction
関数に渡されたラムダ式の中で明示的にロールバックを行う
ロールバックの条件に合致しない場合コミットされます。
明示的なロールバック
setRollbackOnly
関数を呼び出すとwithTransaction
関数終了時にロールバックが実行されます。
db.withTransaction { tx ->
..
tx.setRollbackOnly()
..
}
すでにsetRollbackOnly
関数を呼び出したかどうかはisRollbackOnly
関数で確認できます。
db.withTransaction { tx ->
..
if (tx.isRollbackOnly()) {
..
}
..
}
新規トランザクションの開始と終了
すでに開始されたトランザクションの中で別のトランザクションを新しく開始するにはrequiresNew
関数を呼び出します。
db.withTransaction { tx ->
..
tx.requiresNew {
..
}
..
}
requiresNew
関数にはトランザクションプロパティを指定できます。
db.withTransaction { tx ->
..
tx.requiresNew(transactionProperty = TransactionProperty.IsolationLevel.SERIALIZABLE) {
..
}
..
}
requiresNew
関数の呼び出しが終わるとトランザクションはコミットもしくはロールバックされます。
ロールバックされる条件は次のとおりです。
requiresNew
関数の呼び出しが例外のスローにより終了するrequiresNew
関数に渡されたラムダ式の中で明示的にロールバックを行う
ロールバックの条件に合致しない場合コミットされます。
トランザクショナルなFlow
R2dbcDatabase
はトランザクションを表すFlow
を構築するためのflowTransaction
関数を提供します。
val db = R2dbcDatabase("r2dbc:h2:mem:///example;DB_CLOSE_DELAY=-1")
val transactionalFlow: Flow<Address> = db.flowTransaction {
val a = Meta.address
val address = db.runQuery {
QueryDsl.from(a).where { a.addressId eq 15 }.first()
}
db.runQuery {
QueryDsl.update(a).single(address.copy(street = "TOKYO"))
}
val addressFlow = db.flowQuery {
QueryDsl.from(a).orderBy(a.addressId)
}
emitAll(addressFlow)
}
// Transaction is executed
val list = transactionalFlow.toList()
上記の例では、flowTransaction
の呼び出しはFlow
を構築しているだけです。
トランザクションはtransactionalFlow
を実際に使う際に初めて実行されます。