背景
需要对两个独立的数据库进行读写操作,存在分布式事务问题,所以使用了jta atomikos进行多数据源的事务管理
必要的依赖包及版本
- ‘com.baomidou:mybatis-plus:3.3.2’
- ‘mysql:mysql-connector-java:6.0.6’
- druid-spring-boot-starter’, version: ‘1.1.21’
- spring-boot-starter-jta-atomikos’, version: ‘2.1.6.RELEASE’
配置文件
使用不同的前缀来区分两个数据源
数据源切换思路
持久层框架使用的mybatisplus,项目结构是mapper层下有两个子包,分别存放两个数据源的mapper接口,我希望在mapper层调用方法执行sql之前动态切换数据源,就可以借助aop来拦截具体路径下的方法实现数据源的切换。
代码
DataSourceContextHolder
每个线程维护自己当前的数据源
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
// 设置数据源名
public static void setDataSourceKey(String dbName) {
contextHolder.set(dbName);
}
// 获取数据源名
public static String getDataSourceKey() {
return (contextHolder.get());
}
// 清除数据源名
public static void clearDataSourceKey() {
contextHolder.remove();
}
}
DataSourceNames
标志不同的数据源
public interface DataSourceNames {
String ONE = "platform";
String TWO = "gyl";
}
DataSourceFactory
核心配置类,用于配置多个数据源以及他们的MybatisSqlSessionFactoryBean,注意这里使用的是mybatisplus,所以需要使用MybatisSqlSessionFactoryBean来代替sqlsessionfactory
@Configuration
@MapperScan(basePackages = DataSourceFactory.BASE_PACKAGES, sqlSessionTemplateRef = "sqlSessionTemplate")
public class DataSourceFactory {
static final String BASE_PACKAGES = "com.jqyd.platform.mapper";
private static final String MAPPER_LOCATION = "classpath:mapper/*.xml";
/***
* 创建 DruidXADataSource 用@ConfigurationProperties 自动配置属性
*/
@Bean
@ConfigurationProperties("spring.datasource.druid.platform")
public DataSource druidDataSourceOne() {
return new DruidXADataSource();
}
/***
* 创建 DruidXADataSource
*/
@Bean
@ConfigurationProperties("spring.datasource.druid.gyl")
public DataSource druidDataSourceTwo() {
return new DruidXADataSource();
}
/**
* 创建支持 XA 事务的 Atomikos 数据源 mydbone
*/
@Bean
public DataSource dataSourceOne(DataSource druidDataSourceOne) {
AtomikosDataSourceBean sourceBean = new AtomikosDataSourceBean();
sourceBean.setXaDataSource((DruidXADataSource) druidDataSourceOne);
// 必须为数据源指定唯一标识
sourceBean.setPoolSize(5);
sourceBean.setTestQuery("SELECT 1");
sourceBean.setUniqueResourceName("platform");
return sourceBean;
}
/**
* 创建支持 XA 事务的 Atomikos 数据源 mydbtwo
*/
@Bean
public DataSource dataSourceTwo(DataSource druidDataSourceTwo) {
AtomikosDataSourceBean sourceBean = new AtomikosDataSourceBean();
sourceBean.setXaDataSource((DruidXADataSource) druidDataSourceTwo);
sourceBean.setPoolSize(5);
sourceBean.setTestQuery("SELECT 1");
sourceBean.setUniqueResourceName("gyl_181");
return sourceBean;
}
/**
* @param dataSourceOne 数据源 mydbone
* @return 数据源 mydbone 的会话工厂
*/
@Bean
public MybatisSqlSessionFactoryBean sqlSessionFactoryOne(DataSource dataSourceOne)
throws Exception {
return createSqlSessionFactory(dataSourceOne);
}
/**
* @param dataSourceTwo 数据源 mydbtwo
* @return 数据源 mydbtwo 的会话工厂
*/
@Bean
public MybatisSqlSessionFactoryBean sqlSessionFactoryTwo(DataSource dataSourceTwo)
throws Exception {
return createSqlSessionFactory(dataSourceTwo);
}
/***
* sqlSessionTemplate 与 Spring 事务管理一起使用,以确保使用的