记一次事务失效排查

问题描述

以下方法内部抛出异常后数据库数据没有回滚:

@Override
@Transactional(rollbackFor = Exception.class)
public void updateUserInfo(UserInfo userInfo) {
    userInfo.setUserName("zs");
    //调用mybatis-plus更新方法
    updateById(userInfo);
    //模拟抛出异常
    if(true){
        throw new RuntimeException("异常!");
    }
}
复制代码

排查过程

常见的事务失效场景排查

常见的事务失效场景类似文章挺多这里不在赘述,我是看的这篇文章排查的:聊聊spring事务失效的12种场景,太坑了

源码跟踪

经过以上失效场景排查发现都不符合,于是我们来跟一下事务处理的源码

  1. 先找到Spring事务处理核心方法,它位于spring-tx包下的TransactionAspectSupportinvokeWithinTransaction方法

image.png

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation) throws Throwable {
        // 获取对应事务属性.如果事务属性为空(则目标方法不存在事务)
        final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
     // 根据事务的属性获取beanFactory中的PlatformTransactionManager(spring事务管理器的顶级接口),一般这里或者的是DataSourceTransactiuonManager
        final PlatformTransactionManager tm = determineTransactionManager(txAttr);
     // 目标方法唯一标识(类.方法,如service.UserServiceImpl.save)
        final String joinpointIdentification = methodIdentification(method, targetClass);
     //如果txAttr为空或者tm 属于非CallbackPreferringPlatformTransactionManager,执行目标增强     ①
        if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
            //看是否有必要创建一个事务,根据事务传播行为,做出相应的判断
            TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
            Object retVal = null;
            try {
          //回调方法执行,执行目标方法(原有的业务逻辑)
                retVal = invocation.proceedWithInvocation();
            }
            catch (Throwable ex) {
                // 异常回滚
                completeTransactionAfterThrowing(txInfo, ex);
                throw ex;
            }
            finally {
          //清除信息
                cleanupTransactionInfo(txInfo);
            }
        //提交事务
            commitTransactionAfterReturning(txInfo);
            return retVal;
        }
复制代码
  1. 我们在异常回滚处 completeTransactionAfterThrowing() 方法打断点看看内部回滚执行过程

进入 rollback 方法
image.png
选择 AbstractPlatformTransactionManager
image.png

最终会走到 doRollback()

image.png

进入不同的回滚处理器:

  • mysql使用:DataSourceTransactionManager
  • Mongodb使用:MongoTransactionManager

image.png

  1. 走到这一步的时候我发现异常回滚时只会进入到了MongoDB的事务管理器 MongoTransactionManager 并没有进入mysql的事务管理器,这应该是问题所在,于是找了下项目里事务处理相关配置代码,发现只配置了MongoDB:

image.png

如需要同时支持MongoDB和Mysql事务正确配置为:

@Configuration
public class TransactionConfig {
    @Bean
    MongoTransactionManager mongoTransactionManager(MongoDbFactory mongoDbFactory) {
        return new MongoTransactionManager(mongoDbFactory);
    }

    @Bean
    DataSourceTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    @Primary
    public PlatformTransactionManager chainedTransactionManager(MongoTransactionManager mongoTransactionManager,
                                                                DataSourceTransactionManager dataSourceTransactionManager) {
        return new ChainedTransactionManager(mongoTransactionManager, dataSourceTransactionManager);
    }
}
复制代码

总结

除了常见的事务失效场景,不合理的配置也有可能导致事务不生效,我们在使用不熟悉的配置的时候一定要了解它的作用,以及注意它影响的范围

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享