目 录CONTENT

文章目录

多数据源导致本地事务无效的原因分析及解决方案

成培培
2025-06-18 / 0 评论 / 0 点赞 / 1 阅读 / 0 字

问题现象

最近接手一个项目,和同事一起做,过程中同事问到代码中的声明式事物@Transactional注解怎么不起作用,抛出了异常但是事物并没有回滚

问题原因

起初怀疑是不是出现以下用法问题:

  1. 声明式事物是基于动态代理实现,如果出现在类内部this调用则会绕过动态代理,导致事物失效
  2. @Transactional默认情况下,只会在以下两个异常出现时才会回滚

By default, a transaction will be rolling back on {@link RuntimeException} and {@link Error}

但是根据排查都不属于以上两种情况,但是我发现当前这个项目用的是多数据源,会不会是这个原因,翻看了一下数据源事物自动装配类的源码:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class })
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceTransactionManagerAutoConfiguration {

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnSingleCandidate(DataSource.class)
	static class DataSourceTransactionManagerConfiguration {

		@Bean
		@ConditionalOnMissingBean(PlatformTransactionManager.class)
		DataSourceTransactionManager transactionManager(DataSource dataSource,
				ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
			DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
			transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
			return transactionManager;
		}
	}
}

其中@ConditionalOnSingleCandidate(DataSource.class)这行代码代表只有在单数据源的情况下才会自动装配事务管理器,所以确实是因为多数据源导致的。

解决方案

一般如果用到多数据源的话,可能需要涉及分布式事务管理,但是我们这里业务比较简单不需要分布式事务,只需要在一个库上能实现事务就可以了,我们用的多数据源组件是这个dynamic-datasource,自然也是先翻看官方文档:https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611,比较尴尬的是文档付费观看
所以还是看看源码,DynamicDataSourceAutoConfiguration类中有这么一段:

@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@Bean
@ConditionalOnProperty(prefix = DynamicDataSourceProperties.PREFIX, name = "seata", havingValue = "false", matchIfMissing = true)
public Advisor dynamicTransactionAdvisor() {
    DynamicLocalTransactionInterceptor interceptor = new DynamicLocalTransactionInterceptor();
    return new DynamicDataSourceAnnotationAdvisor(interceptor, DSTransactional.class);
}

当分布式事务seata未配置或者配置false的时候,会装配本地事务拦截的这个切面,拦截@DSTransactional注解的方法,这就明白了,本地事务应该是需要换成这个注解,经测试确实有效。

0

评论区