SpringBoot项目中常见的多数据源管理
一、简介
微服务架构下,数据库常常随着业务拆分会分为多个库。一般涉及不同数据库的查询都是通过微服务间的调用实现,但是例如生成报表和管理后台的业务为了方便一般会给一个微服务配置多个数据源。那么项目中常用的多数据源方案有三种。
二、方案介绍
方案一:使用Spring提供的AbstractRoutingDataSource
使用Spring提供的AbstractRoutingDataSource,该类是springframework.jdbc包下面类,主要是在bean的初始化时构建一个map<Object,Object>结构的数据源配置,并且需要配置spring的DataSourceTransactionManager,这样在和数据库建立连接时就会使用spring的jdbc来连接,而在springjdbc中又会根据map中的key来做路由,不同的key可以使用不同的数据源,因此只需要在执行真正的操作数据库动作前指定路由的key就可以达到数据源的切换。
下面展示部分核心代码:
1.application.yml 配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
datasource1:
url: jdbc:mysql://127.0.0.1:3306/datasource1?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false
username: root
password: root
initial-size: 1
min-idle: 1
max-active: 20
test-on-borrow: true
driver-class-name: com.mysql.cj.jdbc.Driver
datasource2:
url: jdbc:mysql://127.0.0.1:3306/datasource2?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false
username: root
password: root
initial-size: 1
min-idle: 1
max-active: 20
test-on-borrow: true
driver-class-name: com.mysql.cj.jdbc.Driver
2.配置类
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.datasource1")
public DataSource dataSource1() {
// 底层会自动拿到spring.datasource中的配置, 创建一个DruidDataSource
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.datasource2")
public DataSource dataSource2() {
// 底层会自动拿到spring.datasource中的配置, 创建一个DruidDataSource
return DruidDataSourceBuilder.create().build();
}
@Bean
public DataSourceTransactionManager transactionManager1(DynamicDataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
@Bean
public DataSourceTransactionManager transactionManager2(DynamicDataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
DataSourceConfig 主要做了两件事:
- 利用springboot的ConfigurationProperties注解,达到自动解析配置中的两个数据源,并且生成bean.
- 配置DataSourceTransactionManager
3.实现类
@Component
@Primary // 将该Bean设置为主要注入Bean
public class DynamicDataSource extends AbstractRoutingDataSource {
// 当前使用的数据源标识
public static ThreadLocal<String> name=new ThreadLocal<>();
// 写
@Autowired
DataSource dataSource1;
// 读
@Autowired
DataSource dataSource2;
// 返回当前数据源标识
@Override
protected Object determineCurrentLookupKey() {
return name.get();
}
@Override
public void afterPropertiesSet() {
// 为targetDataSources初始化所有数据源
Map<Object, Object> targetDataSources=new HashMap<>();
targetDataSources.put("W",dataSource1);
targetDataSources.put("R",dataSource2);
super.setTargetDataSources(targetDataSources);
// 为defaultTargetDataSource 设置默认的数据源
super.setDefaultTargetDataSource(dataSource1);
super.afterPropertiesSet();
}
}
4.具体的业务方法(部分代码)
@Override
// @WR("W") // 写操作
public void save(Hello hello) {
DynamicDataSource.name.set("W");
helloMapper.save(hello);
}
主要是在执行真正的方法前,通过key来路由到不同的数据源,这个实现也可以通过切面方式实现。
方案二:使用MyBatis注册多个SqlSessionFactory
因为mybatis默认使用的就是单数据源,所有如果想要使用mybatis实现多数据源,就需要我们自己将MyBatis底层的DataSource、SqlSessionFactory、DataSourceTransactionManager这些核心对象一并进行手动注册。这种多数据源的核心是通过路径隔离不同的路径下使用不同的数据源
核心代码如下:
1.application.yml 配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
datasource1:
url: jdbc:mysql://127.0.0.1:3306/datasource1?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false
username: root
password: root
initial-size: 1
min-idle: 1
max-active: 20
test-on-borrow: true
driver-class-name: com.mysql.cj.jdbc.Driver
datasource2:
url: jdbc:mysql://127.0.0.1:3306/datasource2?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false
username: root
password: root
initial-size: 1
min-idle: 1
max-active: 20
test-on-borrow: true
driver-class-name: com.mysql.cj.jdbc.Driver
2.多数据源配置
@Configuration
// 配置不同包路径下的mapper使用不同的数据源
@MapperScan(basePackages = "com.xwl.datasource.dynamic.mybatis.mapper.write",
sqlSessionFactoryRef="wSqlSessionFactory")
public class WMyBatisConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.datasource1")
public DataSource dataSource1() {
// 底层会自动拿到spring.datasource中的配置, 创建一个DruidDataSource
return DruidDataSourceBuilder.create().build();
}
@Bean
@Primary
public SqlSessionFactory wSqlSessionFactory()
throws Exception {
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
// 指定主库
sessionFactory.setDataSource(dataSource1());
return sessionFactory.getObject();
}
@Bean
public DataSourceTransactionManager wTransactionManager(){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource1());
return dataSourceTransactionManager;
}
@Bean
public TransactionTemplate wTransactionTemplate(){
return new TransactionTemplate(wTransactionManager());
}
}
@Configuration
// 配置不同包路径下的mapper使用不同的数据源
@MapperScan(basePackages = "com.xwl.datasource.dynamic.mybatis.mapper.read",
sqlSessionFactoryRef="rSqlSessionFactory")
public class RMyBatisConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.datasource2")
public DataSource dataSource2() {
// 底层会自动拿到spring.datasource中的配置, 创建一个DruidDataSource
return DruidDataSourceBuilder.create().build();
}
@Bean
@Primary
public SqlSessionFactory rSqlSessionFactory()
throws Exception {
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
// 指定主库
sessionFactory.setDataSource(dataSource2());
return sessionFactory.getObject();
}
@Bean
public DataSourceTransactionManager rTransactionManager(){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource2());
return dataSourceTransactionManager;
}
@Bean
public TransactionTemplate rTransactionTemplate(){
return new TransactionTemplate(rTransactionManager());
}
}
方案三:使用dynamic-datasource框架
dynamic-datasource是MyBaits-plus作者设计的一个多数据源开源方案。框架帮我们完成了上面需要手动做的工作,我们只需要引入相关依赖,框架就可以帮我们把不同的数据源配置装配到不同bean,并且提供注解,我们想让某个方法或者某个类中的所有方法使用某一个数据源时可以直接使用注解配置。
1.引入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
2.配置
spring:
datasource:
dynamic:
#设置默认的数据源或者数据源组,默认值即为master
primary: master
#严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
strict: false
datasource:
master:
url: jdbc:mysql://127.0.0.1:3306/datasource1?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false
username: root
password: root
initial-size: 1
min-idle: 1
max-active: 20
test-on-borrow: true
driver-class-name: com.mysql.cj.jdbc.Driver
slave:
url: jdbc:mysql://127.0.0.1:3306/datasource2?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false
username: root
password: root
initial-size: 1
min-idle: 1
max-active: 20
test-on-borrow: true
driver-class-name: com.mysql.cj.jdbc.Driver
3.具体的业务方法
@Override
@DS("master")
public void save(Hello hello) {
helloMapper.save(hello);
}