5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Doma入門 - トランザクション

Last updated at Posted at 2020-10-17

はじめに

データベースアクセスに欠かせないトランザクション機能についてポイントを紹介します。

この記事では、Doma 2.44.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にありますが、ここでは重要な部分を抜粋して示します。

Main.java
public class Main {

  public static void main(String[] args) {
    var config = createConfig();
    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);
        });
  }

  private static Config createConfig() {
    var dialect = new H2Dialect();
    // ①トランザクション対応のデータソースを作る
    var dataSource =
        new LocalTransactionDataSource("jdbc:h2:mem:tutorial;DB_CLOSE_DELAY=-1", "sa", null);
    var jdbcLogger = new Slf4jJdbcLogger();
    // ②トランザクションマネージャーを作る
    var transactionManager = new LocalTransactionManager(dataSource, jdbcLogger);
    // ③Configの実装クラスから上記の①や②で作ったインスタンスを返せるようにする
    return new DbConfig(dialect, dataSource, jdbcLogger, transactionManager); 
  }
}

①トランザクション対応のデータソースを作る

LocalTransactionDataSourceをインスタンス化します。
この例では接続URLなどをコンストラクタで受け取っていますが、DataSourceインスタンスを受け取るコンストラクタも持っています。

②トランザクションマネージャーを作る

上記の①で作ったLocalTransactionDataSourceのインスタンスをコンストラクタに渡してLocalTransactionManagerをインスタンス化します。

③Configの実装クラスから上記の①や②で作ったインスタンスを返せるようにする

①と②で作ったインスタンスをConfigの実装クラスであるDbConfigのコンストラクタに渡してインスタンス化します。

④トランザクションマネージャーのメソッドにラムダ式を渡す

ここのtmは②で作ったLocalTransactionManagerのインスタンスです。
tmrequiredメソッドにトランザクション内で扱いたい処理をラムダ式で渡すことでトランザクションを実行できます。

requiredメソッドはトランザクションがまだ開始されていなかったら開始するメソッドで、他に常に新規にトランザクションを開始するrequiresNewメソッドやトランザクションを一旦停止するnotSupportedメソッドなどがあります。これらのメソッドはネストして使えます。

ラムダ式から例外をスローするかsetRollbackOnlyメソッドを呼び出すかするとトランザクションはロールバックされます。それ以外ではコミットされます。

1つ注意点ですが、ローカルトランザクションの設定をした場合、Domaによる全てのデータベースアクセスは原則的にTransactionManager経由で行う必要があります。そうしない場合、例外が発生します。

おわりに

Domaでトランザクションを利用するポイントを紹介しました。

Domaを使っていてトランザクションがうまく動いていないなと思ったら、この記事をはじめリンク先の記事やサンプルも参考にしてもらえればと思います。

5
5
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
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?