見出し画像

【劔"Tsurugi" Tips集】Webアプリケーションではトランザクション境界となるコンポーネントでTransactionManagerを使う【プログラミングTips】

*****
こちらの記事は、
Tsurugi Advent Calendar 2024(https://adventar.org/calendars/10853
に参加しています。

劔"Tsurugi"関連のエンジニアが12月25日までの期間、さまざまな記事を掲載していきます。

是非、こちらもご覧ください!
*****

Webアプリケーションでは多くの場合、次のような形でコンポーネントをレイヤ化して実装することが多いでしょう。

  • クライアントからの⼊⼒を受け取り、最終的な処理結果をクライアントに返す
    ・ エントリポイントコントローラーと呼ばれることも

  • ドメインロジックを処理するサービス

  • DBと通信し、ドメインオブジェクトの取得と永続化を⾏うリポジトリ
    ・DAO (Data Access Object) と呼ばれることも

そして多くの場合、実装ルールとしてトランザクション境界となるコンポーネントを決めておき (サービスもしくはエントリポイントになるのが⼀般的) 、1トランザクションにまとめるべき処理を同コンポーネントに集約することで、実装者はトランザクション管理を意識しないようにします。

Spring Frameworkを使って実装する場合は @Transactional アノテーションを付与したSpring Beanが、Jakarta EEを使って実装する場合は、EJBのSession Beanもしくは @Transactional アノテーションを付与したCDI Beanがトランザクション境界となり、トランザクションのコミットやロールバックといったトランザクションの制御はフレームワーク側が⾏います。

これらフレームワークが提供するトランザクション管理機構はJDBCやJTAといった既存のAPIを前提に構築されているため、Tsurugiでは利⽤することができません。代わりにTsurugiでは TsurugiTransactionManager というトランザクション管理クラスを⽤意しているので、これを利⽤します。

Spring Frameworkを⽤いて実装する場合の例を⽰します。サービスコンポーネント(@Service アノテーションを付与して明⽰しています)をトランザクション境界とする場合、その実装は次のようになります (リポジトリに加え、 TsurugiConnector もSpring Bean化してインジェクトしています)。

@Service
public class ExampleService {

    private final TsurugiConnector tsurugiConnector;

    private final ExampleRepository repository;

    public ExampleService(TsurugiConnector tsurugiConnector, ExampleRepository
      repository) {
             this.tsurugiConnector = tsurugiConnector;
              this.repository = repository;
    }

    public ExampleResponse doSomeLogic(ExampleInput input) {
       try (TsurugiSession session = tsurugiConnector.createSession()) {
          var tm =
session.createTransactionManager(TgTmSetting.ofAlways(TgTxOption.ofOCC(), 2));
        return tm.execute(transaction -> {
          // ここに1トランザクションにまとめるべき処理を実装して結果を返す
          // リポジトリには引数のtransactionオブジェクトを渡す
           });
       } catch (IOException | InterruptedException e) {
        throw new RuntimeException(e);
       }
   }
}

継承を前提としたサービスコンポーネントの共通基底クラスを作ったり、AOPを利⽤するなどしてTsurugiTransactionManager を使ったトランザクション管理を、実装側から隠蔽するようにしてもいいかもしれません。

リポジトリコンポーネントは次のようにメソッド引数で TsurugiTransaction を受けるようにし、サービス側で管理しているトランザクションに参加します。

トランザクション実⾏時に送出される例外はそのまま呼び出し、元にスローして処理をゆだねるようにするといいでしょう。

@Repository
public class ContractRepository {

   public List<ExampleEntity> findBySomeCondition(TsurugiTransaction transaction,
ExampleInput input)
       throws TsurugiTransactionException, IOException, InterruptedException {
       // TsurugiSessionオブジェクトはTsurugiTransactionから取得する
       TsurugiSession session = transaction.getSession();

// 以降で取得したセッションを使ってDBとのIO処理を実装

   }
}

次世代高速RDB劔"Tsurugi"は、オープンソースで公開中です。
ぜひダウンロードしてみて、触ってみてください。

ダウンロードはコチラから
https://www.tsurugidb.com/

いいなと思ったら応援しよう!