问题:
在业务需求中需要两个数据源做支撑,接口中需要从两个数据源中获取数据以及修改数据
原来我只在mapper层加了数据源的切换
Mapper层:
方法上加注解 @DataSource(value = DataSourceType.SLAVE)
然后因为涉及到修改,所以在service层加了事务 @Transactional
结果导致报错: "xxx表 doesn't exist"
原因:
1、Spring@Transactional不支持跨数据源事物,Spring 事物控制是基于数据库链接进行的,当数据源切换后,数据库链接切换,事物回滚只能回退,当前持有的链接。
2、Spring开启事物后,会将当前数据库及数据库链接资源进行线程绑定,导致数据源切换失效(数据源切换执行后,并未获取到新的数据库链接)。
简单来说: 就是当你添加了事务,Spring会在方法执行时切换数据源之前去先做事务绑定,此时会绑定到默认的也就是主数据源连接上,然后当代码执行到从数据源时,事务依然是来自于主数据源
所以我们的解决办法就是指定事务所在的数据源连接
解决方案:
网上有人说 @Transactional(propagation = Propagation.REQUIRES_NEW) 来修改事务的传播方式,当每进行一次方法时单独开启一个事务
但是我试了下是没有用的
还有一个方法是在数据源配置类中去配置两个数据源的事务管理对象,然后在事务注解中去指定事务管理器对象,这个方法也是我目前使用的方法.不过我不知道当两个数据源都需要修改数据时,怎么保证两个数据源的数据都能正常回滚,业务中没有涉及到也就没有来得及去做测试校验
1. 创建数据源的事务管理对象
@Bean
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource masterDataSource(DruidProperties druidProperties)
{
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return druidProperties.dataSource(dataSource);
}
@Bean(name = "slaveDataSource")
@ConfigurationProperties("spring.datasource.druid.slave")
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
public DataSource slaveDataSource(DruidProperties druidProperties)
{
DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
return druidProperties.dataSource(dataSource);
}
@Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource dataSource(DataSource masterDataSource)
{
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");
return new DynamicDataSource(masterDataSource, targetDataSources);
}
@Bean(name = "masterTransactionManager")
@Primary
public PlatformTransactionManager transactionManager(@Qualifier("masterDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "slaveTransactionManager")
public PlatformTransactionManager secondaryTransactionManager(@Qualifier("slaveDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
2. 配置文件中指定默认的数据源事务管理器
# 指定默认的事务管理器和数据源
spring:
jpa:
transaction-manager-name: transactionManager
3.方法上添加事务注解,并根据需求指定事务管理器对象
@Transactional(transactionManager = "slaveTransactionManager")