Spring Boot Mybatis 多数据源
SpringBoot 提供了动态数据源抽象类 AbstractRoutingDataSource
,通过该类可以实现动态数据源切换。类中定义了一个Map<Object,DataSource>
类型的数据源集合,通过key获取对应的数据源。
java
//AbstractRoutingDataSource源码
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
@Nullable
private Map<Object, DataSource> resolvedDataSources; // map类型的数据源集合,通过key获取对应的数据源
...
@Override
public Connection getConnection() throws SQLException {
return determineTargetDataSource().getConnection();
}
protected DataSource determineTargetDataSource() {
...
Object lookupKey = determineCurrentLookupKey(); // 获取key
DataSource dataSource = this.resolvedDataSources.get(lookupKey); // 通过key获取对应的数据源
...
return dataSource;
}
@Nullable
protected abstract Object determineCurrentLookupKey(); // 获取key的方法,可以通过实现该方法来自定义我们的数据源获取逻辑
}
自定义DataSource
定义一个实现AbstractRoutingDataSource
类的动态数据源,同时定义一个通过ThreadLocal
实现线程隔离的动态数据源切换。
java
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceHolder.getDataSource();
}
}
java
public class DataSourceHolder {
private static final ThreadLocal<String> DATA_SOURCES = new ThreadLocal<>();
public static void setDataSource(String dataSource) {
DATA_SOURCES.set(dataSource);
}
public static String getDataSource() {
return DATA_SOURCES.get();
}
public static void removeDataSource() {
DATA_SOURCES.remove();
}
}
SpringBoot 注册多数据源
注册多数据源实例,并设置到动态数据源中。
java
@MapperScan(basePackages = "com.demo.dao", sqlSessionFactoryRef = "sqlSessionFactory") // Mybatis扫描Mapper
@Configuration
public class DatasourceConfig {
@ConfigurationProperties("spring.datasource.test1") // 根据前缀获取配置并构建实例
@Bean("test1DataSource")
public DataSource test() {
return DataSourceBuilder.create().build();
}
@ConfigurationProperties("spring.datasource.test2")
@Bean("test2DataSource")
public DataSource test2() {
return DataSourceBuilder.create().build();
}
@Bean("dynamicDataSource")
public DynamicDataSource dynamicDataSource(@Qualifier("test1DataSource") DataSource test1DataSource,
@Qualifier("test2DataSource") DataSource test2DataSource) {
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("test1", test1DataSource);
dataSourceMap.put("test2", test2DataSource);
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setTargetDataSources(dataSourceMap); // 将数据源集合设置到动态数据源中
return dynamicDataSource;
}
@Bean("sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DynamicDataSource dynamicDataSource)
throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource); // 设置动态数据源到sqlSessionFactory
sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
return sqlSessionFactoryBean.getObject();
}
}
yml
spring:
datasource:
test1:
jdbc-url: jdbc:mysql://localhost:3306/test1?characterEncoding=utf-8
username: dev
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
test2:
jdbc-url: jdbc:mysql://localhost:3306/test2?characterEncoding=utf-8
username: dev
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
Aop 切换数据源
可以通过定义一个Aop切面的方式去切换数据源,例如:根据登录的租户信息切换数据源。
java
@Aspect
@Configuration
public class DataSourceAspect {
//设置aop的切入点,在controller层就设置
@Pointcut("execution(* com.demo.web..*.*(..))")
public void dataSourcePointcut(){
}
@Before("dataSourcePointcut()")
public void before(){
//切换数据源,条件可以从用户的登录信息,或者是从入参传入等方式进行数据源的切换,也可以不在aop中进行设置数据源
DataSourceHolder.setDataSource("test");
}
@After("dataSourcePointcut()")
public void after(){
//执行完成时清除数据源
DataSourceHolder.removeDataSource();
}
}