项目:人人开源 多数据源
<dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>2.5.5</version> </dependency>
问题: 多数据源在事务下不生效
问题原因: spring检测到如果开启了事务,会将Connection缓存起来,然后在事务再执行第二条sql时候,继续使用之前缓存好的Connection
临时解决方案:
1.不在事务内进行多数据源的切换(废话)
2.添加注解,当前方法不在事务内运行
//当前方法不会运行在事务内,如果在事务内,则将事务挂起
@Transactional(propagation = Propagation.NOT_SUPPORTED)
3.异步(网上答案,亲测失败)
PS.以上方案在分布式中无效
源头解决方案:
1、从源头解决问题,我们在往Spring容器注册事务管理器的时候,就可以更改其默认的事务管理器,从而Spring在选择到该数据源的时候就知道用哪一个事务管理器了。下面是相应的配置代码
@Autowired @Qualifier("ADataSource") private DataSource ADS; @Bean public PlatformTransactionManager uiTransactionManager() { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(ADS); return transactionManager; }
2、在使用 Transactional 注解的时候,我们会发现里面有一个值叫 value,也就是 transactionManager 的别名,在这里指定加载的事务管理器,也可以达到指定的效果。代码如下:
@Configuration @EnableTransactionManagement @MapperScan(basePackages = "*.*.*.B",sqlSessionTemplateRef = "BSqlSessionTemplate") public class BDataSourceConfig { @Autowired @Qualifier("BDataSource") private DataSource BDS; @Bean public SqlSessionFactory mgSqlSessionFactory() throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); tk.mybatis.mapper.session.Configuration configuration = new tk.mybatis.mapper.session.Configuration(); configuration.setMapUnderscoreToCamelCase(true); bean.setConfiguration(configuration); bean.setDataSource(BDS); bean.setTypeAliasesPackage("*.*.*.B"); bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:*.xml")); return bean.getObject(); } // Spring注册Bean中,方法名即为Bean的名字 @Bean public PlatformTransactionManager BTransactionManager() { return new DataSourceTransactionManager(BDS); } @Bean public SqlSessionTemplate BSqlSessionTemplate(@Qualifier("BSqlSessionFactory") SqlSessionFactory sqlSessionFactory) { return new SqlSessionTemplate(sqlSessionFactory); } } --------配置代码结束-------- -------业务代码开始--------- @Transactional(value = "BTransactionManager") public void test(){ } -------业务代码结束---------
3、手动控制事务管理器,和上面一种方案类似,但是这种方案更具源码性吧,不用了Spring原生的 @Transactional 而改用自己实现的方式:
// 由于是多数据源,因此会有多个事务管理器,因此用Resource根据名称的注入方式更为合理 @Resource PlatformTransactionManager ATransactionManager; @Transactional(rollbackFor = Exception.class) public boolean addMenu(MenuRequest request){ TransactionStatus status = ATransactionManager.getTransaction(new DefaultTransactionDefinition()); try { /** 业务代码 **/ }catch (Exception e){ log.error("addMenu {}",e); ATransactionManager.rollback(status); return false; } ATransactionManager.commit(status); return true; }