事务传播行为

7种传播行为

REQUIRED(默认)

propagation写在子方法上,对子方法生效(所有传播方式都是这样)

REQUIRED_NEW

如下图,对purchase注释REQUIRED_NEW,第一个purchase执行成功后,第二个purchase如果执行失败,不会回滚第一个。

但是第二个purchase抛出了异常,checkout里没有对其捕获,或导致checkout回滚,就是说如果purchase前有直接操作数据库的非事务,会被回滚(由于checkout是事务,拦截到了purchase抛出来的异常)。

死锁:使用REQUIRED_NEW要特别注意死锁!

以下代码为例,transaction接口transactionNewHelloWorld做了如下事情

1.调用helloDao.updateNameByIdhello表进行了修改;

2.调用helloWorldService.updateHelloWorldhello表和world表进行了修改。

外部只要调用transactionNewHelloWorld 这个方法就会发生死锁,因为外部事务先对hello表加锁了,内部事务REQUIRES_NEW会新开一个事务,想要获得hello的锁,但是获得不到,产生死锁。

如果将上面两步换一下就不会发生死锁了。

https://stackoverflow.com/questions/13051204/spring-transaction-required-vs-requires-new-rollback-transaction

If you really need to do it in separate transaction you need to use REQUIRES_NEW and live with the performance overhead. Watch out for dead locks.

I'd rather do it the other way:

  • Validate data on Java side.

  • Run everyting in one transaction.

  • If anything goes wrong on DB side -> it's a major error of DB or validation design. Rollback everything and throw critical top level error.

  • Write good unit tests.

REQUIRED_NEWREQUIRED异常回滚

GitHub Code: feat: 对REQUIRED和REQUIRES_NEW异常捕获测试

假设只有BBBe3处会抛出异常,且一定会抛出异常

  • e1:在MAIN里(e1处)捕获异常,一定能捕获得到,且不会再抛出异常。这里是否捕获异常不会影响update的结果

  • e3:在此处捕获异常相当于没有抛出异常,update函数正常运行。

  • e2:只在此处捕获异常

  • e123都没捕获:(两种结果是一样的,和e2 p1相比,只有抛的异常不一样)。

    • p1:最终结果是全部没有执行(update的1和helloworld的1都被回滚了)

    • p2:最终结果是全部没有执行(helloworld的1被回滚,没有执行到3这里来;update的1由于helloworld抛出的异常而被回滚)。

默认RuntimeExceptionError 导致回滚

没试过:即使没写RuntimeException,也会包括其中

https://stackoverflow.com/questions/12830364/does-specifying-transactional-rollbackfor-also-include-runtimeexception

No need to include RuntimeException in rollbackFor list. It will handle that even if you do not mention it.

I've tried it out for jdbcTemplate:-

noRollbackFor的问题没搞懂

在一个非transaction方法里调同类的另一个transaction方法,transaction不生效;

法1.需要@Resource注入+context.getBean调用

法2.如果不用@Resource注入的话,则继承ApplicationContextAware

Last updated

Was this helpful?