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