メインコンテンツへスキップ
見出し画像

【劔"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/

    あなたへのおすすめ