问题描述
以下方法内部抛出异常后数据库数据没有回滚:
@Override
@Transactional(rollbackFor = Exception.class)
public void updateUserInfo(UserInfo userInfo) {
userInfo.setUserName("zs");
//调用mybatis-plus更新方法
updateById(userInfo);
//模拟抛出异常
if(true){
throw new RuntimeException("异常!");
}
}
复制代码
排查过程
常见的事务失效场景排查
常见的事务失效场景类似文章挺多这里不在赘述,我是看的这篇文章排查的:聊聊spring事务失效的12种场景,太坑了
源码跟踪
经过以上失效场景排查发现都不符合,于是我们来跟一下事务处理的源码
- 先找到Spring事务处理核心方法,它位于spring-tx包下的TransactionAspectSupport类 invokeWithinTransaction方法
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;
}
复制代码
- 我们在异常回滚处 completeTransactionAfterThrowing() 方法打断点看看内部回滚执行过程
进入 rollback 方法
选择 AbstractPlatformTransactionManager
最终会走到 doRollback()
进入不同的回滚处理器:
- mysql使用:DataSourceTransactionManager
- Mongodb使用:MongoTransactionManager
- 走到这一步的时候我发现异常回滚时只会进入到了MongoDB的事务管理器 MongoTransactionManager 并没有进入mysql的事务管理器,这应该是问题所在,于是找了下项目里事务处理相关配置代码,发现只配置了MongoDB:
如需要同时支持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