はじめに
データベースアクセスに欠かせないトランザクション機能についてポイントを紹介します。
この記事では、Doma 3.6.0を前提とします。
Domaの他の機能紹介についてはDoma入門もお読みください。
最初に検討すること
トランザクションに関して最初に検討すべきことがあります。
それは、利用しているフレームワーク(SpringFrameworkなど)がトランザクション機能を提供しているかどうかです。提供している場合、まずはそちらの利用を検討ください。
SpringFrameworkを使う場合
SpringFrameworkはトランザクション機能を提供します。
org.seasar.doma.jdbc.Configの実装クラスのgetDataSourceメソッドでは、org.springframework.jdbc.datasource.TransactionAwareDataSourceProxyを使ってラップしたDataSourceを返すように設定してください。これが非常に重要です。
上記対応を行った上でこのガイドを参考にSpringのコンポーネントのクラスやメソッドに@Transactionalを付与すればトランザクションを利用できます。
doma-spring-bootを使えば上記のDataSourceのラッピングは自動で行われます。doma-spring-bootを使ってSpringFrameworkのトランザクション機能を利用しているサンプルアプリとしてはspring-boot-jpetstoreがあります。
Quarkusを使う場合
Quarkusはトランザクション機能を提供します。
org.seasar.doma.jdbc.Configの実装クラスのgetDataSourceメソッドでは、Quarkusのコネクションプール実装であるAgroalで管理されたDataSourceを返してください。
上記対応を行った上でこのドキュメントにあるようにCDIのコンポーネントのクラスやメソッドに@Transactionalを付与すればトランザクションを利用できます。
Quarkus Doma Extensionを使えば上記のDataSourceの設定は自動で行われます。Quarkus Doma Extensionを使ってQuarkusのトランザクション機能を利用しているサンプルアプリとしてはquarkus-sampleがあります。
トランザクション機能を提供するフレームワークを使わない場合
Domaのローカルトランザクションの利用を検討してください。
Domaのローカルトランザクションの特徴は次の通りです。
-
ThreadLocalを使ってスレッドごとにコネクションを管理する - 名前の通りローカルトランザクションなので扱えるリソースは1つだけ(グローバルトランザクションのように2フェーズコミットの機能はない)
- 手続的なAPIである
org.seasar.doma.jdbc.tx.TransactionManagerを提供する(@Transactionalのような宣言的なAPIではない) - JDBCのセーブポイント機能を提供する
使い方は次の節で述べます。
Domaのローカルトランザクション
動くコードはgetting-startedにありますが、ここでは重要な部分を抜粋して示します。
public class Main {
public static void main(String[] args) {
var config =
SimpleConfig.builder("jdbc:h2:mem:tutorial;DB_CLOSE_DELAY=-1", "sa", null)
.jdbcLogger(new Slf4jJdbcLogger())
.build();
var tm = config.getTransactionManager();
// setup database
var appDao = new AppDaoImpl(config);
tm.required(appDao::create);
// read and update
tm.required(
() -> {
var repository = new EmployeeRepository(config);
var employee = repository.selectById(1);
employee.age += 1;
repository.update(employee);
});
}
}
Domaのローカルトランザクションを使う場合、SimpleConfigを使ってConfigを生成することをお勧めします。SimpleConfigを使えば、ローカルトランザクションを利用するための設定が自動で行われます。
config.getTransactionManager()で取得できるtmはLocalTransactionManagerのインスタンスです。tmのrequiredメソッドにトランザクション内で扱いたい処理をラムダ式で渡すことでトランザクションを実行できます。
requiredメソッドはトランザクションがまだ開始されていなかったら開始するメソッドで、他に常に新規にトランザクションを開始するrequiresNewメソッドやトランザクションを一旦停止するnotSupportedメソッドなどがあります。これらのメソッドはネストして使えます。
ラムダ式から例外をスローするかsetRollbackOnlyメソッドを呼び出すかするとトランザクションはロールバックされます。それ以外ではコミットされます。
おわりに
Domaでトランザクションを利用するポイントを紹介しました。
Domaを使っていてトランザクションがうまく動いていないなと思ったら、この記事をはじめリンク先の記事やサンプルも参考にしてもらえればと思います。