C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

SpringのTransactionalについて

[トピック内 7 記事 (1 - 7 表示)]  << 0 >>

■100570 / inTopicNo.1)  SpringのTransactionalについて
  
□投稿者/ ぼぼ (1回)-(2022/09/20(Tue) 15:55:52)

分類:[Java] 

こんにちは。
SpringBoot2.6.6、JDK1.8で開発してます。

下記の様な2つのServiceを呼んだ処理を実装し、正常系では@DB登録AMQ送信は正しく動いてます。
ここでMQ側でエラーを起こ(MQサーバを落とす)し、その場合@のDB登録もロールバックしてくれるよう、Serviceそれぞれに@Transactionalを
メソッドに付与しているのですが、ロールバックされてません。

ぐぐるとメソッドは必ずpublic、ExceptionはRuntimeException以下のものでなければ関知しないとの事でラップしているつもりなのですが、コミットされてしまいます。。

こちら、何が悪いかご存じの方いらっしゃいましたら、宜しくお願いします。

●Controller
@RestController
public class TestController {

  @Autowired
  private MqService mqService;

  @Autowired
  private DbService dbService;

  @GetMapping("/a")
  public JsonResult execute_a(@Validated ARequestInfo form, BindingResult validate) throws Exception {
    JsonResult result = new JsonResult();
    // 複数件登録
    for (int i = 0; i < 3; i++) {
      try {
        // @DB登録
        String[] params = {String.valueOf(i + 1), form.getName(), form.getAge()};
        dbService.insert(params);

        // AMQ送信
        mqService.execute(form);
        result.setResult("success");
      } catch (Exception e) {
        e.printStackTrace();
        result.setResult("error");
//        break;
      }
    }

    return result;
  }


●Service1
@Configuration
@Service
public class DbService {
  @Autowired
  private JdbcTemplate jdbcTemplate;

  @Transactional
  public void insert(String[] values) {
    String sql = "insert into test_user values(";
    for (int i = 0 ; i < values.length; i++) {
        sql = sql + " ?,";
    }
    sql = sql.substring(0, sql.length() - 1);
    sql = sql + ")";
    jdbcTemplate.update(sql, values);
  }
}

●Service2
@Service
public class MqService {
  @Autowired
  private sendGateway gateway;

  @Transactional
  public void execute(Object requestInfo) throws RuntimeException {
    try {
      gateway.execute(message);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
}



引用返信 編集キー/
■100571 / inTopicNo.2)  Re[1]: SpringのTransactionalについて
□投稿者/ 伝説のカレー (39回)-(2022/09/20(Tue) 17:24:48)
No100570 (ぼぼ さん) に返信

トランザクションを管理してるオブジェクトは例外が投げられたことをどうやって検知するんでしょうね
execute_a内で例外が捕まえられてexecute_aは正常終了するのでコミットされるのはそのせいなんじゃないですかね
execute_aにtry-catchを書かなければいんじゃないかなって思いました

引用返信 編集キー/
■100572 / inTopicNo.3)  Re[1]: SpringのTransactionalについて
□投稿者/ とっちゃん (766回)-(2022/09/20(Tue) 17:27:04)
No100570 (ぼぼ さん) に返信

> こちら、何が悪いかご存じの方いらっしゃいましたら、宜しくお願いします。
>
dbService, mqService のいずれかで例外が出たらそれを TestController.execute_a の呼び出し元に伝搬せず
result に "error" をセットして終了するというプログラムになっていますが、
dbService や mqService それぞれに対してはエラー処理を行っているようには見受けられません。

今の形では、execute_a のなかで発生した何等かのエラーは dbService や mqService は知ることができないままとなっています。

どういう形でエラーを伝搬する必要があるかについてはどういう段取りで取りやめするのかで変わるので
わかりませんが(全部まとめてキャンセルなのか、3回ループのうちエラーが発生した1回だけキャンセルなのかでも変わるはず)
execute_a の catch ブロック内で dbService.insert() や mqService.execute() の取り消しを要求しない限り
ロールバック(あるいはそれに相当する処理)は発生することはないと思います。

引用返信 編集キー/
■100573 / inTopicNo.4)  Re[2]: SpringのTransactionalについて
□投稿者/ 伝説のカレー (40回)-(2022/09/20(Tue) 17:37:20)
@Transactionalのアノテーションが何を表すかなんですが
ググったところでは、@Transactionalがつけられたメソッドはそのメソッドが呼ばれたときに
トランザクションが開始され、例外が投げられたらロールバックされるって感じですね

そうするとDbServiceのトランザクションとMqServiceのトランザクションは独立してる可能性もありますね
DbServiceとMqServiceを使うサービスを作ってそっちのメソッドに@Transactionをつける必要があるのかもしれないです

execute_aで例外を投げてもコミットされるようだったら↑これかもしれないです
引用返信 編集キー/
■100574 / inTopicNo.5)  Re[3]: SpringのTransactionalについて
□投稿者/ ぼぼ (2回)-(2022/09/21(Wed) 00:13:26)
ありがとうございます。アドバイスありがとうございます。
伝説のカレーさんの仰る通り、@Transactionalの使い方が間違って?いました。

>ググったところでは、@Transactionalがつけられたメソッドはそのメソッドが呼ばれたときに
>トランザクションが開始され、例外が投げられたらロールバックされるって感じですね

mqServiceとdbServiceの@Transactionalを排除し、もう一つ中間にServiceを配置し、これに@Transactionalを付与する事で
@の処理もロールバックされました。


●Service0
@Service
public class TestService {

  @Autowired
  private MqService mqService;

  @Autowired
  private DbService dbService;

  @Transactional
  public void insert(Object requestInfo) throws RuntimeException {
      try {
        // @DB登録
        String[] params = {form.getName(), form.getAge()};
        dbService.insert(params);

        // AMQ送信
        mqService.execute(form);
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }
  }


ただ、検証時に気付いたのですが、DB側のAutoCommit設定もfalseにしないとコミットされてしまう様でした。
正確にはSpring側のコネクション管理(プール等)なので、DB側では無いのですが。。

DBのトランザクションはロールバックできましたが、MQ送信の方はロールバックされませんでした。
質問の趣旨とずれてしまいますが、MQ側のコネクション管理でもAutoCommit設定はありますでしょうか?
Gatewayで乗せてますが、送信自体はSpringのJmsTemplateを使用しています。


引用返信 編集キー/
■100576 / inTopicNo.6)  Re[4]: SpringのTransactionalについて
□投稿者/ 伝説のカレー (41回)-(2022/09/21(Wed) 10:40:06)
No100574 (ぼぼ さん) に返信

https://ryoasai.hatenadiary.org/entry/20101110/1289397211

sessionTransactedをtrueにすれば良いみたいな情報はありました
なんか他にもやり方あるみたいです
どうやるのがいんでしょうね
引用返信 編集キー/
■100577 / inTopicNo.7)  Re[5]: SpringのTransactionalについて
□投稿者/ ぼぼ (4回)-(2022/09/21(Wed) 16:21:58)
No100576 (伝説のカレー さん) に返信
> sessionTransactedをtrueにすれば良いみたいな情報はありました
> なんか他にもやり方あるみたいです
> どうやるのがいんでしょうね

ありがとうございます。
こちら指定する事でMQもロールバックできました。
(MQはactiveMQ5.7を使用しており、他では動作差異あるかもです)

グローバルトランザクションのXA、チェインTransactionManagerなど複数ありますね。。
内部的にはそれぞれのDataSourceを順にコミットするのは変わらないっぽいですが・・

色々調べて頂き、ありがとうございました。
解決した為、閉じさせていただきます。
解決済み
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ