记录一次Spring事务线上异常
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
Spring事务管理配置方式
- XML模糊匹配绑定事务管理
- 注解可对每个需要进行事务处理的方法单独配置只需 @Transactional然后添加属性配置
为简便本文使用注解方式。Spring初始化时会通过扫描拦截对事务的方法进行增强。若目标方法存在事务Spring就会创建一个Bean对应的代理Proxy对象并进行相关的事务处理操作。
1 setup
1.1 数据库配置文件 jdbc.properties
1.2 JDBC配置类
从 jdbc.properties 加载相关配置项并创建 JdbcTemplate、DataSource、TransactionManager 等相关Bean。
1.3 应用配置类
通过注解配置了数据源、MyBatis Mapper 的扫描路径以及事务等。
2 unchecked 异常与事务回滚
用户管理功能每位用户注册后都往数据库里存入信息
Mapper类
数据库表Schema
业务类 StudentService包括一个保存的方法 saveStudent。执行一下保存一切正常。
测试该事务是否回滚若发现用户名=JavaEdge抛异常触发事务回滚。
测试保存我这个用户
执行结果打印出了这样的信息
异常抛了但观察到DB还是有条新记录。
那为何异常也抛了却没有回滚
3 源码解析
顺着 saveUser debug
看到 CglibAopProxy事务本质上也是一种特殊切面在创建过程中被 CglibAopProxy 代理。
事务处理拦截器是 TransactionInterceptor支撑整个事务功能的架构
TransactionInterceptor如何实现事务特性
执行代理类的目标方法时触发invoke()
。跳到异常处理。catch到异常时调 completeTransactionAfterThrowing进一步处理。
TransactionAspectSupport#invokeWithinTransaction
completeTransactionAfterThrowing.rollbackOn()
事务回滚判断条件。条件满足时即会触发事务回滚。
RuleBasedTransactionAttribute#rollbackOn()
RuleBasedTransactionAttribute自身的rollbackOn()
当在 @Transactional 配置 rollbackFor该方法就会用捕获到的异常和 rollbackFor 中配置的异常比对
- 所捕获异常是 rollbackFor 配置异常直接 rollback
案例中没有加任何规则所以找不到规则去处理所以 winner == null
进而走到下一步。
DefaultTransactionAttribute 的 rollbackOn()
当发生如下 case
- 没有在 @Transactional 配置 rollback 属性
- 或者捕获到的异常和所配置异常类型不一
就调用父类rollbackOn()
只有异常类型为 RuntimeException 或 Error才true
=》才触发 completeTransactionAfterThrowing#rollback
=》事务才回滚
综上Spring 处理事务时若没有在 @Transactional 配置 rollback 属性则只有捕获到 RuntimeException 或 Error 才会触发事务回滚。
而案例抛 Exception又未指定回滚规则所以未触发回滚。
4 修正
将所抛异常类型改成 RuntimeException
这种修改方法不优雅毕竟异常有时就是固定死不能修改。还有更好方案。
解析 RuleBasedTransactionAttribute#rollbackOn 的代码时提到过 rollbackFor 属性的处理规则。在 @Transactional 的 rollbackFor 加入要支持的异常类型在这是 Exception即可匹配上我们所抛异常。完善注解配置即可