Springcloud+Mybatis使用多数据源的四种方式
前段时间在做会员中心和中间件系统开发时,多次碰到多数据源配置问题,主要用到分包方式、参数化切换、注解+AOP、动态添加 这四种方式。这里做一下总结,分享下使用心得以及踩过的坑。
分包方式
数据源配置文件
在yml中,配置两个数据源,id分别为master和s1。
spring:
datasource:
master:
jdbcUrl: jdbc:mysql://192.168.xxx.xxx:xxxx/db1?.........
username: xxx
password: xxx
driverClassName: com.mysql.cj.jdbc.Driver
s1:
jdbcUrl: jdbc:mysql://192.168.xxx.xxx:xxxx/db2?........
username: xxx
password: xxx
driverClassName: com.mysql.cj.jdbc.Driver
复制代码
数据源配置类
master数据源配置类
注意点:
需要用@Primary注解指定默认数据源,否则spring不知道哪个是主数据源;
@Configuration
@MapperScan(basePackages = "com.hosjoy.xxx.xxx.xxx.xxx.mapper.master", sqlSessionFactoryRef = "masterSqlSessionFactory")
public class MasterDataSourceConfig {
//默认数据源
@Bean(name = "masterDataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.master")
public HikariDataSource masterDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
@Bean(name = "masterSqlSessionFactory")
@Primary
public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource datasource, PaginationInterceptor paginationInterceptor)
throws Exception {
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
bean.setDataSource(datasource);
bean.setMapperLocations(
// 设置mybatis的xml所在位置
new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/master/**/**.xml"));
bean.setPlugins(new Interceptor[]{paginationInterceptor});
return bean.getObject();
}
@Bean(name = "masterSqlSessionTemplate")
@Primary
public SqlSessionTemplate masterSqlSessionTemplate(
@Qualifier("masterSqlSessionFactory") SqlSessionFactory sessionfactory) {
return new SqlSessionTemplate(sessionfactory);
}
}
复制代码
s1数据源配置类
@Configuration
@MapperScan(basePackages = "com.hosjoy.xxx.xxx.xxx.xxx.mapper.s1", sqlSessionFactoryRef = "s1SqlSessionFactory")
public class S1DataSourceConfig {
@Bean(name = "s1DataSource")
@ConfigurationProperties(prefix = "spring.datasource.s1")
public HikariDataSource s1DataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
@Bean(name = "s1SqlSessionFactory")
public SqlSessionFactory s1SqlSessionFactory(@Qualifier("s1DataSource") DataSource datasource
, PaginationInterceptor paginationInterceptor)
throws Exception {
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
bean.setDataSource(datasource);
bean.setMapperLocations(
// 设置mybatis的xml所在位置
new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/s1/**/**.xml"));
bean.setPlugins(new Interceptor[]{paginationInterceptor});
return bean.getObject();
}
@Bean(name = "s1SqlSessionTemplate")
public SqlSessionTemplate s1SqlSessionTemplate(
@Qualifier("s1SqlSessionFactory") SqlSessionFactory sessionfactory) {
return new SqlSessionTemplate(sessionfactory);
}
}
复制代码
使用
可以看出,mapper接口、xml文件,需要按照不同的数据源分包。在操作数据库时,根据需要在service类中注入dao层。
特点分析
优点
实现起来简单,只需要编写数据源配置文件和配置类,mapper接口和xml文件注意分包即可。
缺点
很明显,如果后面要增加或删除数据源,不仅要修改数据源配置文件,还需要修改配置类。
例如增加一个数据源,同时还需要新写一个该数据源的配置类,同时还要考虑新建mapper接口包、xml包等,没有实现 “热插拔” 效果。
参数化切换方式
思想
参数化切换数据源,意思是,业务侧需要根据当前业务参数,动态的切换到不同的数据源。
这与分包思想不同。分包的前提是在编写代码的时候,就已经知道当前需要用哪个数据源,而参数化切换数据源需要根据业务参数决定用哪个数据源。
例如,请求参数userType值为1时,需要切换到数据源slave1;请求参数userType值为2时,需要切换到数据源slave2。
/**伪代码**/
int userType = reqUser.getType();
if (userType == 1){
//切换到数据源slave1
//数据库操作
} else if(userType == 2){
//切换到数据源slave2
//数据库操作
}
复制代码
设计思路
数据源注册
数据源配置类创建datasource时,从yml配置文件中读取所有数据源配置,自动创建每个数据源,并注册至bean工厂和AbstractRoutingDatasource(后面聊聊这个),同时返回默认的数据源master。
数据源切换
(1)通过线程池处理请求,每个请求独占一个线程,这样每个线程切换数据源时互不影响。
(2)根据业务参数获取应切换的数据源ID,根据ID从数据源缓存池获取数据源bean;
(3)生成当前线程数据源key;
(4)将key设置到threadLocal;
(5)将key和数据源bean放入数据源缓存池;