LoginSignup
16
17

More than 5 years have passed since last update.

SpringBootでトランザクション処理を実装しよう

Last updated at Posted at 2019-02-16

トランザクション処理を実装してみましょう。
SpringBootではどうやればいいのでしょうね。

トランザクション処理は@Transactional アノテーションで

例えばこんな感じの実装の時、insert処理に対してトランザクションをかけたいとします。
(もし全部のソースコードみたい方は最後の方にgitlabのURL貼ってますのでそちらからどうぞ)

// IndexController.java

@Controller
public class IndexController {

  :

  @RequestMapping
    public String index(Model model) {
    // do something,
  }

  @RequestMapping(value = "/insert")
  public String insert(Model model, @RequestParam("name") String name) {
    // do something.
  }

  :
}

以下のように insertメソッドの前に @Transactional アノテーションを追加するだけです。

あ、import org.springframework.transaction.annotation.Transactional; も必要ですね。

// IndexController.java

import org.springframework.transaction.annotation.Transactional; // ←これと

@Controller
public class IndexController {

  :

  @RequestMapping
    public String index(Model model) {
    // do something,
  }

  @Transactional // ← これを追加!
  @RequestMapping(value = "/insert")
  public String insert(Model model, @RequestParam("name") String name) {
    // do something.
  }

  :
}

これでinsertメソッド中の実行中に例外が発生すると自動的にロールバックをしてくれます。簡単ですね。

何か気をつけることはあるのかな。

ロールバックしない例外がある

ただし、ロールバックの注意点として、非検査例外(RuntimeException及びそのサブクラス)が発生した場合はロールバックされるが、検査例外(Exception及びそのサブクラスでRuntimeExceptionのサブクラスじゃないもの)が発生した場合はロールバックされずコミットされる。

via: Springでトランザクション管理

ほう。試してみましょう。例外の制御をしやすいのでupdate処理に例外処理をいれます。デモページの[果物の名前を変更する > 変更後] のテキストエリアに非検査例外検査例外と入力すると、それぞれの例外をスローする処理を追加しました。

tran1.jpeg

// IndexController.java

@Controller
public class IndexController {

  :

  @Transactional
    @RequestMapping(value = "/update")
    public String insert(Model model, @RequestParam("chg_before_name") String before_name,
            @RequestParam("chg_after_name") String after_name) throws Exception {

        fruitMapper.update(before_name, after_name);

        List<Fruit> list = fruitMapper.selectAll();
        model.addAttribute("fruits", list);

        // トランザクション動作確認     
        if(after_name.equals("非検査例外")) {
            //  非検査例外の時にはロールバックが発生します。      
            System.out.println("非検査例外発生");
            throw new RuntimeException();
        }
        if(after_name.equals("検査例外")) {
            // 検査例外の時にはそのままコミットされます。          
            System.out.println("検査例外発生");
            throw new Exception();
        }
        return "index";
    }

    // 例外キャッチ
    @ControllerAdvice
    public class ExceptionHandlerAdvisor {
         @ExceptionHandler
         public String catchError(RuntimeException e){
            System.out.println("非検査例外キャッチ");
            return "error";
         }
         @ExceptionHandler
         public String catchError(Exception e){
            System.out.println("検査例外キャッチ");
            return "error";
         }
    }
  :
}

動作確認

非検査例外、検査例外それぞれの動きを確認してみましょう。

非検査例外のとき

hi-kensa.gif

mysql> select * from fruits;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | みかん    |
|  2 | ばなな    |
|  3 | りんご    |
+----+-----------+
3 rows in set (0.00 sec)

-- アップデート実行するが、非検査例外である RuntimeException がスローされる。

mysql> select * from fruits;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | みかん    |
|  2 | ばなな    |
|  3 | りんご    |
+----+-----------+
3 rows in set (0.01 sec)

みかん非検査例外に更新されるかと思いますが、トランザクション処理中の例外発生によりロールバックされています。つまり、更新処理はなかったことにされていますね。

検査例外のとき

kensa.gif

mysql> select * from fruits;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | みかん    |
|  2 | ばなな    |
|  3 | りんご    |
+----+-----------+
3 rows in set (0.00 sec)

-- アップデート実行するが、検査例外である Exception がスローされる。

mysql> select * from fruits;
+----+--------------+
| id | name         |
+----+--------------+
|  1 | 検査例外     |
|  2 | ばなな       |
|  3 | りんご       |
+----+--------------+
3 rows in set (0.00 sec)

非検査例外の時と同じように "みかん" が "検査例外" に更新されないことを期待していましたが、今回スローした例外は検査例外のためコミットされてしまいました。

こういう動きがあることを知っておかないと危ないですね。

実際に動かしてみたい方

自分のSpringBootプロジェクト環境を仲間に渡そう を参考に、以下のリポジトリをクローンしてもらえれば動作すると思います。

# STSプロジェクトを入手します
git clone -b demo_transaction https://gitlab.com/msrx9/sample_springboot_crud.git

# MySQL環境を入手します
git clone https://gitlab.com/msrx9/sample_docker_mysql.git

参考にさせてもらった記事

16
17
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
16
17