Help us understand the problem. What is going on with this article?

使い方を間違えると動作しない@Transactionalを動作させる使い方

More than 1 year has passed since last update.

Springアノテーション「@Transactional」はメソッドにつけるだけで自動的にトランザクションを行い、標準でRuntimeExceptionが発生した場合にロールバックを行ってくれる便利なものです。
しかし、@Transactionalをつけただけではトランザクションを行われない場合があり、ビルドエラーにもならないので実際に動かしたらトランザクションしていなかったということがありました。
ということで、@Transactionalを動作させるための使い方をまとめました。

@Transactionalが動作するための条件

いろいろ動かして確認してみたところ、下記の条件を満たす必要があるようです。

  • DIしているクラスのpublicメソッドであること
  • DIしている別のクラスもしくはフレームワークから直接呼ばれていること

DIしているクラスのpublicメソッド

クラスをDIする方法としてBean定義ファイルを使う方法もありますが、ここではSpringアノテーションを使用した実装を記載しておきます。

ServiceClass.java
@Service
public class ServiceClass {

    @Transactional
    public void updateDB() {
        // DB更新処理
    }
}

上記では@Serviceをクラスにつけておりますが、@Controller@RepositoryなどのDIを行う他のアノテーションでも動作します。

DIしている別のクラスから直接呼ぶ

上記のServiceClassを呼び出すクラスが下記となります。

ControllerClass.java
@RequestMapping("/sample/*")
@Controller
public class ControllerClass {

    @Autowired
    ServiceClass serviceClass;

    @RequestMapping(value = { "index" })
    public String index(){
        serviceClass.updateDB();

        return "sample/index";
    }
}

こちらも@Controllerを使用してDIしているクラスです。
@Autowiredを使用してServiceClassのインスタンスを自動で作成し、updateDBメソッドを呼び出すことでトランザクションが動作します。

フレームワークから直接呼ぶ

Springのフレームワークから@Transactionalをつけているメソッドが呼び出された場合も動作します。
下記の場合は、@RequestMappingの設定に該当するリクエストが来てメソッドが呼び出されることでトランザクションが動作します。

ControllerClass.java
@RequestMapping("/sample/*")
@Controller
public class ControllerClass {

    @Autowired
    ServiceClass serviceClass;

    @RequestMapping(value = { "index" })
    @Transactional
    public String index(){
        serviceClass.updateDB();

        return "sample/index";
    }
}
ServiceClass.java
@Service
public class ServiceClass {

    public void updateDB() {
        // DB更新処理
    }
}

また、@Scheduledによる時間指定でのメソッド呼び出しでも同様にトランザクションが動作します。

ControllerClass.java
@Controller
public class ControllerClass {

    @Autowired
    ServiceClass serviceClass;

    @Scheduled(cron = "0 0 10 * * *")
    @Transactional
    public String index(){
        serviceClass.updateDB();

        return "sample/index";
    }
}

実装例

実用的な実装を考えると、RuntimeException以外の例外が発生した場合もロールバックしたいので
@Transactional(rollbackFor = Exception.class)としてExceptionおよびExceptionを継承しているクラスがthrowされるとロールバックされるように設定します。
呼び出し元のメソッドでtry-catchして成功、失敗で処理を分けます。

ControllerClass.java
@RequestMapping("/sample/*")
@Controller
public class ControllerClass {

    @Autowired
    ServiceClass serviceClass;

    @RequestMapping(value = { "exec" })
    public String exec(Model model){

        try {
            // トランザクションを行うメソッドの呼び出し
            serviceClass.transaction();

            // 成功した場合の処理
            model.addAttribute("message", "処理に成功しました。");
        } catch (Exception e) {
            // 処理に失敗した場合の処理
            if (e.getMessage() != null) {
                 model.addAttribute("message", e.getMessage());
            } else {
                 model.addAttribute("message", "エラーが発生しました。");
            }
            return "sample/index";
        }
        return "sample/complete";
    }
}
ServiceClass.java
@Service
public class ServiceClass {

    @Transactional(rollbackFor = Exception.class)
    public void transaction() throws Exception {
        // トランザクションしたい一連の処理
        // ロールバックしたい場合は下記のようにExceptionをthrowする
        throw new Exception("処理に失敗しました。");
    }
}
kojiita
i-enter
「効果」をつねに提供します。スマホアプリ開発No.1の実績。最新のIoTに対応した開発も行います。
https://www.i-enter.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした