需求背景
因产品业务需要,需使用 2 种数据库对数据进行分库存储(也可以是多个)。要求在尽量不改动原有代码的基础上实现多数据库配置和自动切换数据源。
技术选型
dynamic-datasource:dynamic-datasource-spring-boot-starter 是一个基于springboot的快速集成多数据源的启动器。其支持 Jdk 1.7+, SpringBoot 1.4.x 1.5.x 2.x.x。
通过集成dynamic-datasource,仅需要在类或方法上方添加 @DS(“数据源名称”),即可实现数据源的自动切换。
框架文档:https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611
集成步骤
1、引入依赖
<!--多数据源-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.6.1</version>
</dependency>
2、配置多数据源
spring:
datasource:
dynamic:
druid:
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
primary: system # 默认数据源
datasource:
system: # 数据源名称,可随意定义
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://******
username: ***
password: ******
druid:
validationQuery: SELECT 1 FROM DUAL
local: # 数据源2
driver-class-name: org.sqlite.JDBC
url: jdbc:sqlite:******
username:
password:
druid:
validationQuery: SELECT 'x'
3、配置SqlSessionFactory
由于业务数据源和本地数据源使用的数据库类型不同,其mapper.xml文件和所需要的拦截器等也有所不同,就需要配置两个SqlSessionFactory。那么如何随着数据源的切换,匹配到相对应的SqlSessionFactory是接下来需要考虑的问题。
实现方案:定义两个SqlSessionTemplate,分别为两个数据源的mapper指定对应的SqlSessionTemplate。这样执行到mapper接口的方法时,就会自动去对应的SqlSessionTemplate中获取SqlSessionFactory
@Configuration
// ※ 关键:使用@MapperScan注解,将Mapper接口文件和SqlSessionTemplate绑定
@MapperScan(basePackages = "com.xx.xx.*.mapper", sqlSessionTemplateRef = "systemSqlSessionTemplate")
public class DataSourceSystemConfig {
@Bean
public SqlSessionFactory systemSqlSessionFactory(DataSource dataSource, MyBatisProperties myBatisProperties) throws Exception {
MybatisSqlSessionFactoryBean sessionFactoryBean = new MybatisSqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
//通用拦截器、类型转换器
setMybatisSqlSessionFactoryBean(sessionFactoryBean,myBatisProperties);
// 设置Mapper文件的扫描路径
String mapperLocations = "classpath:mapper/system/*/*Mapper.xml";
// 添加拦截器等组件
sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
return sessionFactoryBean.getObject();
}
@Bean
public SqlSessionTemplate systemSqlSessionTemplate(@Qualifier("systemSqlSessionFactory") SqlSessionFactory systemSqlSessionFactory) {
return new SqlSessionTemplate(systemSqlSessionFactory);
}
}
@Configuration
@MapperScan(basePackages = "com.xx.xx.*.mapper.local", sqlSessionTemplateRef = "localSqlSessionTemplate")
public class DataSourceLocalConfig {
@Bean
public SqlSessionFactory localSqlSessionFactory(DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean sessionFactoryBean = new MybatisSqlSessionFactoryBean();
// 数据源
sessionFactoryBean.setDataSource(dataSource);
// 设置Mapper文件的扫描路径
// 数据源(本地数据库已定为sqlite,所以直接指定)
String mapperLocations = "classpath:mapper/local/*/*Mapper.xml";
sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mapperLocations));
// 添加拦截器等组件
setMybatisSqlSessionFactoryBean(sessionFactoryBean);
return sessionFactoryBean.getObject();
}
@Bean
public SqlSessionTemplate localSqlSessionTemplate(@Qualifier("localSqlSessionFactory") SqlSessionFactory localSqlSessionFactory) {
return new SqlSessionTemplate(localSqlSessionFactory);
}
}
5、编码使用
- 在使用默认数据源时,正常编码无需手动指定数据源
- 在使用其他数据源时,需要在方法或类上通过 @DS 注解指定数据源
- 注解优先级:方法注解 > 类注解
- 最佳方案:直接加在 Mapper 类上
应用场景举例:
@Service
@DS("system") // 除指定了数据源的方法外,类中所有方法均使用system数据源
public class MyService {
@Autowired
private MyMapper myMapper;
@DS("system") //默认数据源,可以不指定
public void methodWithDataSource1() {
// 在此方法中使用system数据源
myMapper.someMethod();
}
@DS("local")
public void methodWithDataSource2() {
// 在此方法中使用local数据源
myMapper.someMethod();
}
}