spring事务注解@Transactional

1 简述

spring为规范事务处理,提供了注解@Transactional,让编写事务逻辑,更简单、直观、清晰,也更规范。

注解@Transactional的配置参数,除支持数据库本身的隔离级别外,还增加了传递规则等概念,下面会详细解读。

本文对@Transactional使用中,常见问题、注意事项进行整理,尤其对逻辑上存在嵌套关系的事务,在出现异常时,分场景进行了详细分析,具体看5.2分析内容。

2 参数

@Transactional包含参数:

transactionManager 当系统中有多个事务管理器时,该属性可以指定匹配哪个事务管理器,如果只有一个事务管理器,系统会默认匹配。
propagation 事务的传播规则,默认值为 REQUIRED。
isolation 事务的隔离度,默认值为 DEFAULT,也就是跟当前所用的数据库的默认隔离级别一致。

timeout 超时时间, 单位秒,  是所注解方法的总耗时,如果超时,就会抛超时异常,并直接回滚该事务,默认值-1, 无限制。
read-only 表明事务对DB的操作是否为只读,默认为false, 如果为true, 则只允许对数据库查询,不允许修改数据,否则抛异常。

rollbackFor 配置需回滚的受检异常类,  默认spring只对继承自RuntimeException、Error的异常进行回滚,受检异常不会回滚.
noRollbackFor 配置不需要回滚的异常类。

3 隔离级别

枚举类Isolation(隔离级别),包含选项:

DEFAULT 默认,跟底层数据库的设置隔离级别一致,数据库设置的是什么就用什么。

READ_UNCOMMITTED 读未提交,该级别事务可以看到其他事务未提交的数据,存在脏读、幻读、不可重复读。
READ_COMMITTED 读已提交,该级别的事务不会看到其他事务未提交的数据,存在幻读,不可重复读。
REPEATABLE_READ 可重复读,该级别事务,可以保证读过的数据,即使其他事务已更新提交,也可以读到原来的值,仍存在幻读。(mysql数据库默认级别)
SERIALIZABLE 串行化,隔离级别最高,性能最弱,不存在脏读、幻读、不可重复读问题。

4 传递规则

枚举类Propagation,包含选项:

名词描述是否事务DB链接
REQUIRED默认事务传递级别,配置该注解的方法,如果被包含在其他事务上下文中,则该方法逻辑会合并到当前事务,否则会新建事务(采用新的数据库链接)。共享
SUPPORTS配置该注解的方法,如果被包含在其他事务逻辑中,则该方法逻辑会合并到当前事务上下文中,否则不会新建事务,跟普通非事务方法一样。可能共享
MANDATORY必须被包含在其他事务上下文中,自己不会新建发起事务,否则抛异常共享
REQUIRES_NEW不管是否被包含在其他事务上下文中,自己都会新建独立的新事务(即采用新的数据库链接)。独立
NOT_SUPPORTED不管是否被包含在其他事务上下文中,都会采用自己独立的数据库链接,且以普通非事务方式处理。独立
NEVER必须以普通非事务方式处理,不允许被包含在其他事务逻辑中,否则抛异常独立
NESTED

配置为该注解的方法,如果被包含在其他事务上下文中,则该方法的逻辑会作为"内嵌子事务"被包含在当前事务,它们共享一个数据库链接资源,否则会新建事务。

正常情况:跟REQUIRED一样,

回滚情况:注解NESTED事务的方法回滚, 不会影响包含它的上级事务, 上级事务仍然可以正常提交;而注解REQUIRED事务的方法回滚, 它的上级事务不能正确提交,也必须回滚。

共享

事务传递性本质,就是一个事务遇上另个一个事务二者如何协调
或者更准确的说,一个被标记@Transactional的方法被包含在事务上下文中,该方法如何跟包含它的事务上下文协调,互相融洽、共享当前数据库链接资源,还是自己仍然单干(独立申请数据库链接资源);二者只相遇(视若无睹),还是势同水火。

不得不说,spring对事务传递性抽象很精炼,道出了事物与事物之间协作的本质,人与人之间的协作也一样,没什么不同。


5 回滚

5.1 @Transactional参数: rollbackFor,noRollbackFor

spring默认只对继承自RuntimeException、Error的异常进行回滚处理,对于继承自Exception的受检异常,事务不会自动回滚,如果需要回滚处理,需要对相关异常进行配置。
例如: @Transactional(rollbackFor={Exception.class})

5.2 事务间的影响


条件:只有在逻辑上,存在事务包含关系,且事务间共享一个数据库链接资源的情况,其中一个事务回滚,才有可能会导致整个事务上下文不能提交,只能回滚。

如果事务方法独占一个数据库链接资源,事务回滚就只跟它自己有关系,不会影响其他事务方法。


通过上面对事务传递规则梳理,其中REQUIRED、SUPPORTS、MANDATORY、NESTED这4中传递类型会跟上下文共享一个"数据库链接资源",也就是说这四种事务注解方法的事务回滚,会导致事务上下文不能提交,只能回滚,但其中NESTED是个例外,它是内嵌事务,可以只回滚自己,整个事务上下文仍可以正常提交。

提示:事务回滚是由该方法边界之外,spring事务拦截逻辑处理,方法内部只是屏蔽、吞掉异常,不会进行事务提交、回滚。


例:
假如在逻辑上,事务方法A包含事务方法B,且共享一个事务资源(NESTED除外),且A是整个事务的最外层边界。

场景一: A内有异常,B运行正常

方式1:如果该异常被A内部采用try逻辑给吞掉,整个事务仍会正常提交。
方式2:如果该异常没有被A吞掉,spring 会拦截该异常,依据上面说的异常回滚规则,进行提交或回滚。

场景二: A内正常,B内有异常

方式1:如果该异常被B内部采用try逻辑给吞掉,整个事务仍会正常提交。

方式2:如果该异常被A采用try逻辑给吞掉,事实上在B方法的逻辑边界,spring已拦截B的异常,并把事务标记为"回滚",此时整个事务已不能正常提交。如果提交就会出现如下异常:
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

场景三: A、B内都有异常

如果A的异常A自己内部吞掉,B的异常B自己内部吞掉,整个事务仍会正常提交,否则整个事务回滚。

:尽管异常可以在事务方法内部给吞掉,这样事务就可以被正常提交,这并不代表符合业务逻辑规则,通常大多数情况是不符合业务规则的。

6 常见问题

6.1 异步处理

被标记@Transactional方法的内部,如果有异步处理逻辑,异步方法会申请独立的数据库链接资源,跟当前事务上下文,没有事务关系。

6.2 内部调用

Spring中,事务注解@Transactional是由AOP实现的,通过运行时动态生成代理对象,对目标对象进行增强,负责对事务进行开启、提交、回滚操作。
如果在一个类的内部,调用自己的方法,就算该方法被@Transactional注解,事务不会生效,需要通过它的代理类调用才可以,如下:

        MyService myService = (MyService)AopContext.currentProxy();
        myService.doSomething();    

6.3 事务相互影响

两个@Transactional注解的事务方法,如果存在逻辑包含关系,如何判断二者是否存在相互影响,就看它们是共享一个DB事务,还是各自独立的DB事务,细节可以参考“5.2 事务间的影响”部分内容。

7 总结

除了上面整理的常见问题,注意事项,在整个事务上下文运行期间,它会独占珍贵的"数据库链接资源",原则上,内部逻辑应尽量简单、轻量,避免耗时逻辑。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值