つかびーの技術日記

情報系修士卒のWeb系技術日記です。現在のフォーカス分野はアドテクです。

ScalikeJDBCのTxBoundaryの使い方

      2014/12/22

こんにちは、@s_tsukaです。

今日は自分の好きなライブラリの1つであるScalikeJDBCのTxBoundaryについて触れて行きます!

ScalikeJDBCのトランザクション

皆さん、ScalikeJDBCを使うときは併せてトランザクションを使用しているかと思います。

例えばこんな感じで。

 引用:scalikejdbc-cookbook https://github.com/scalikejdbc/scalikejdbc-cookbook/blob/master/ja/04_transaction.md#localtx

localTxで囲ったブロック内で例外が発生するとrollbackします。

これはこれで素晴らしい機能なのですが、あるシーンで少し問題が出てきます。

例外が発生せずに終了する場合

例外が発生せずに終了する場合はcommitされます。「別にcommitでいいじゃん!」とも思えるのですが、Scalaコードの場合、結果をEitherで返す場合があるかと思います。

例えば以下のコードのような感じで。

まずはbuild.sbtです。ライブラリを使います。

以下は実際のコードです。h2dbを使っているのでメモリ上にDBやテーブルが一時的にできます。

DBとテーブル作ってinsertして最後にselectしています。

callSomethingは結果をEitherで返しています。

以下は実行結果です。

処理は成功するので、当然値が取れます。

ここでcallSomethingメソッドを少し考えます。ここがものすごく複雑なロジックで途中で例外も発生すれば何らかのチェック処理でエラーだと判断することもあるとします。

そういう場合、ScalaではEitherというものを使うことがあります。EitherはRightまたはLeftという値を取ることができて、Rightなら成功、Leftなら失敗という意味になります。

上記のケースだと必ずRightを返していますが、コメントアウトを逆転してLeftを返すようにしてみます。つまり処理失敗です。

こんな風にLeftを返すようにしてみました。

この状態で実行するとLeft、つまり失敗を返している(さらにLeftの中は例外!)にも関わらずPersonが1件取れてしまいます。

これはなぜかと言うとlocalTxはあくまで例外が投げられたときにrollbackするからです。localTxの気持ち的には「Leftとか例外じゃないし、別に正常終了じゃん?commitだよね」という感じです。

例外以外のケースでもrollbackさせたいときはTxBoundary

そこで今回の主役であるTxBoundaryの出番です。

結論から言うと、こう書きます。するとRightを返したときはcommitしますが、Leftのときはrollbackします。

以下が実行結果です。

import文1行なのでお手軽ですね。

どうしてTxBoundaryでrollbackするのか

ソースを見ると割とすぐに分かります。

https://github.com/scalikejdbc/scalikejdbc/blob/develop/scalikejdbc-core/src/main/scala/scalikejdbc/TxBoundary.scala

matchしてLeftのときはtx.rollback()しています。これのおかげですね。

独自クラスでTxBoundaryしたい

例えばscalazのValidationなんかはTxBoundaryに対応していません。また、独自にResultTypeみたいな型を作って、それを戻り値型にしているケースなども対応していません。

けれど、自分で作ってしまえば大丈夫です。

例えば以下のような型があるとします。

callSomethingの戻り値型がResultTypeで、SuccessかWarnのときはcommit, その他の場合はrollbackしたいとします。

こんな感じで独自トランザクション制御処理を作ります。

後は以下のようにimportするものを変えて、

callSomethingの戻り値型もResultTypeに変えれば上手く動きます。

この場合はErrorTypeなので、rollbackされます。

以上で終わりです。TxBoundary便利なので使いましょう!

 - Scala, ライブラリ , ,