一、前言
emm,又又又踩坑啦。这次的需求主要是对逾期计算的需求任务进行优化,现有的计算任务运行时间太长了。简单描述下此次的问题:在项目中进行多个数据库执行操作时,我们期望的是将其整个封装成一个事务,要么全部成功,或者全部失败,然而在自测异常场景时发现,里面涉及的第一个数据状态更新成功了,但是后面的数据在插入出现异常,后面查询数据表发现,该数据的状态已经被更新成功啦。
emmm,查看代码发现确实是使用了@Transactional注解没问啊。于是通过查询网上相关资料发现,在使用Spring中事务注解@transactional时会存在几种场景下该注解失效,即不能按照预期封装成一个事务操作,于是对该注解进行学习并对相关失效场景进行分析,整理文章如下;
二、@Transactional注解失效场景实例验证
1、@Transactional注解属性
属性 | 类型 | 描述 |
value | String | 可选的限定描述符,指定使用的事务管理器 |
propagation | Enum:Propagation· | 可选的事务传播行为设置 |
isolation | Enum:Isolation | 可选的事务隔离级别设置 |
readOnly | boolean | 读写或只读事务,默认读写 |
timeout | int | 事务超时时间设置 |
rollbackFor | Class对象数组,必须继承自Throwable | 导致事务回滚的异常类数组 |
rollbackForClassName | 类名数组,必须继承自Throwable | 导致事务回滚的异常类名字数组 |
noRollbackFor | Class对象数组,必须继承自Throwable | 不会导致事务回滚的异常类数组 |
noRollbackForClassName | 类名数组,必须继承自Throwable | 不会导致事务回滚的异常类名字数组 |
2、 propagation属性
propagation代表事务的传播行为,默认值为Propagation.REQUIRED
属性 | 描述 |
Propagation.REQUIRED | 若当前存在事务则加入该事务,若不存在则创建一个新事务(默认) |
Propagation.SUPPORTS | 若当前存在事务则加入该事务,若不存在则以非事务的方式继续进行 |
Propagation.MANDATORY | 若当前存在事务则加入该事务,若不存在则抛出异常 |
Propagation.REQUIRES_NEW | 重新创建一个新的事务,若当前存在事务则暂定当前事务 |
Propagation.NOT_SUPPORTED | 以非事务的方式运行,若当前存在事务则暂定当前事务 |
Propagation.NEVER | 以非事务的方式运行,若当前存在事务则抛出异常 |
Propagation.NESTED | 与Propagation.REQUIRED效果一样 |
3、 @Transactional注解使用场景?
@Transactional注解可以作用在接口、类、类方法中。
◦当作用于类时,表示所有该类的public方法都配置相同的事务属性信息。
◦当作用于方法时,当类配置了@Transactional注解,方法也配置了@Transactional,方法的事务会覆盖类的事务配置信息。
◦当作用于接口时,不推荐使用,因为在接口使用@Transactional并且配置了Spring AOP使用CGLib动态代理将会导致其失效。
4、 @Transactional注解失效场景?
◦@Transactional注解作用在非public修饰的方法上,会失效。
失效原因:在Spring AOP代理时,TransactionInterceptor(事务拦截器)在目标方法执行前后进行拦截,DynamicAdvisedInterceptor(CglibAopProxy的内部类)的Intercept方法或JDKDynamicAOPProxy的invoke方法会间接调用AbstractFallbackTransationAttributeSource的computeTransactionAttribute方法,获取@Transactional注解的事务配置信息。
1 protected TransactionAttribute computeTransactionAttribute(Method method,
2 Class<?> targetClass) {
3 // Don't allow no-public methods as required.
4 if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
5 return null;
6 }
此方法会检查目标方法的修饰符是否为public,非public作用域则不会获取@transactional的属性配置信息。其中protected、private修饰的方法上使用@Transactional注解,事务会失效但不会有任何报错。
◦@Transactional注解属性propagation设置错误导致注解失效
失效原因:配置错误, PROPAGATION_SUPPORTS、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER三种事务传播方式不会发生回滚。
▪实例验证:写了一个demo进行测试。demo主要功能如下:执行两次数据库插入操作,并在扩展信息字段中添加备注;
▪运行结果如下,构造的单号不存在订单查询为空触发异常,观察数据库发现,第一次数据库插入操作已经执行成功,故而验证@Transactional注解失效;