使⽤dynamic-datasource-spring-boot-starter做多数据源。。。
多数据源系列
3、使⽤dynamic-datasource-spring-boot-starter做多数据源及源码分析
⽂章⽬录
简介
前两篇博客介绍了⽤基本的⽅式做多数据源,可以应对⼀般的情况,但是遇到⼀些复杂的情况就需要扩展下功能了,⽐如:动态增减数据源、数据源分组,纯粹多库 读写分离 ⼀主多从、从其他数据库或者配置中⼼读取数据源等等。其实就算没有这些需求,使⽤这个实现多数据源也⽐之前使⽤AbstractRoutingDataSource要便捷的多
dynamic-datasource-spring-boot-starter 是⼀个基于springboot的快速集成多数据源的启动器。
github:
⽂档:
它跟mybatis-plus是⼀个⽣态圈⾥的,很容易集成mybatis-plus
特性:
1. 数据源分组,适⽤于多种场景 纯粹多库 读写分离 ⼀主多从 混合模式。
2. 内置敏感参数加密和启动初始化表结构schema数据库database。
3. 提供对Druid,Mybatis-Plus,P6sy,Jndi的快速集成。
4. 简化Druid和HikariCp配置,提供全局参数配置。
5. 提供⾃定义数据源来源接⼝(默认使⽤yml或properties配置)。
6. 提供项⽬启动后增减数据源⽅案。
7. 提供Mybatis环境下的 纯读写分离 ⽅案。
8. 使⽤spel动态参数解析数据源,如从session,header或参数中获取数据源。(多租户架构神器)
9. 提供多层数据源嵌套切换。(ServiceA >>> ServiceB >>> ServiceC,每个Service都是不同的数据源)
10. 提供 不使⽤注解 ⽽ 使⽤ 正则 或 spel 来切换数据源⽅案(实验性功能)。
11. 基于seata的分布式事务⽀持。
实操
先把坐标丢出来
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
下⾯抽⼏个⽤的⽐较多的应⽤场景介绍
基本使⽤
使⽤⽅法很简洁,分两步⾛
⼀:通过yml配置好数据源
⼆:service层⾥⾯在想要切换数据源的⽅法上加上@DS注解就⾏了,也可以加在整个service层上,⽅法上的注解优先于类上注解
spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict:false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候回抛出异常,不启动会使⽤默认数据源.
datasource:
master:
url: jdbc:mysql://127.0.0.1:3306/dynamic
username: root
password:123456
driver-class-name: sql.jdbc.Driver
db1:
url: jdbc:gbase://127.0.0.1:5258/dynamic
username: root
springboot结构password:123456
driver-class-name: com.gbase.jdbc.Driver
这就是两个不同数据源的配置,接下来写service代码就⾏了
# 多主多从
spring:
datasource:
dynamic:
datasource:
master_1:
master_2:
slave_1:
slave_2:
slave_3:
如果是多主多从,那么就⽤数据组名称_xxx,下划线前⾯的就是数据组名称,相同组名称的数据源会放在⼀个组下。切换数据源时,可以指定具体数据源名称,也可以指定组名然后会⾃动采⽤负载均衡算法切换
# 纯粹多库(记得设置primary)
spring:
datasource:
dynamic:
datasource:
db1:
db2:
db3:
db4:
db5:
纯粹多库,就⼀个⼀个往上加就⾏了
@Service
@DS("master")
public class UserServiceImpl implements UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
public List<Map<String, Object>>selectAll(){
return jdbcTemplate.queryForList("select * from user");
}
@Override
@DS("db1")
public List<Map<String, Object>>selectByCondition(){
return jdbcTemplate.queryForList("select * from user where age >10");
}
}
注解结果
没有@DS默认数据源
@DS(“dsName”)dsName可以为组名也可以为具体某个库的名称
通过⽇志可以发现我们配置的多数据源已经被初始化了,如果切换数据源也会看到打印⽇⼦的
是不是很便捷,这是官⽅的例⼦
集成druid连接池
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.22</version>
</dependency>
⾸先引⼊依赖
spring:
autoconfigure:
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
再排除掉druid原⽣的⾃动配置
datasource:#数据库链接相关配置
dynamic:
druid:#以下是全局默认值,可以全局更改
#监控统计拦截的filters
filters: stat
#配置初始化⼤⼩/最⼩/最⼤
initial-size:1
min-idle:1
max-active:20
#获取连接等待超时时间
max-wait:60000
#间隔多久进⾏⼀次检测,检测需要关闭的空闲连接
time-between-eviction-runs-millis:60000
#⼀个连接在池中最⼩⽣存的时间
min-evictable-idle-time-millis:300000
validation-query: SELECT 'x'
test-while-idle:true
test-on-borrow:false
test-on-return:false
#打开PSCache,并指定每个连接上PSCache的⼤⼩。oracle设为true,mysql设为false。分库分表较多推荐设置为false
pool-prepared-statements:false
max-pool-prepared-statement-per-connection-size:20
stat:
merge-sql:true
log-slow-sql:true
slow-sql-millis:2000
primary: master
datasource:
master:
url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=GMT%2B8
username: root
password: root
driver-class-name: sql.cj.jdbc.Driver
gbase1:
url: jdbc:gbase://127.0.0.1:5258/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&useSSL=false&ze roDateTimeBehavior=convertTo
Null
username: gbase
password: gbase
driver-class-name: com.gbase.jdbc.Driver
druid:# 以下参数针对每个库可以重新设置druid参数
initial-size:
validation-query: select 1 FROM DUAL #⽐如oracle就需要重新设置这个
public-key:#(⾮全局参数)设置即表⽰启⽤加密,底层会⾃动帮你配置相关的连接参数和filter。
配置好了就可以了,切换数据源的⽤法和上⾯的⼀样的,打@DS(“db1”)注解到service类或⽅法上就⾏了
详细配置参考这个配置类com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties servic
e嵌套
这个就是特性的第九条:提供多层数据源嵌套切换。(ServiceA >>> ServiceB >>> ServiceC,每个Service都是不同的数据源)
借⽤源码中的demo:实现SchoolService >>> studentService、teacherService
public class SchoolServiceImpl{
public void addTeacherAndStudent(){
teacherService.addTeacherWithTx("ss",1);
teacherMapper.addTeacher("test",111);
studentService.addStudentWithTx("tt",2);
}
}
@Service
@DS("teacher")
public class TeacherServiceImpl {
public boolean addTeacherWithTx(String name, Integer age){
return teacherMapper.addTeacher(name, age);
}
}
@Service
@DS("student")
public class StudentServiceImpl {
public boolean addStudentWithTx(String name, Integer age){
return studentMapper.addStudent(name, age);
}
}
这个addTeacherAndStudent调⽤数据源切换就是primary ->teacher->primary->student->primary
关于其他demo可以看官⽅wiki,⾥⾯写了很多⽤法,这⾥就不赘述了,重点在于学习原理。。。
为什么切换数据源不⽣效或事务不⽣效?
这种问题常见于上⼀节service嵌套,⽐如serviceA -> serviceB、serviceC,serviceA
加上@Transaction
简单来说:嵌套数据源的service中,如果操作了多个数据源,不能在最外层加上@Transaction开启事务,否则切换数据源不⽣效,因为这属于分布式事务了,需要⽤seata⽅案解决,如果是单个数据源(不需要切换数据源)可以⽤@Transaction开启事务,保证每个数据源⾃⼰的完整性
下⾯来粗略的分析加事务不⽣效的原因:
它这个切换数据源的原理就是实现了DataSource接⼝,实现了getConnection⽅法,只要在service中开启事务,service中对其他数据源操作只会使⽤开启事务的数据源,因为开启事务数据源会被缓存下来,可以在DataSourceTransactionManager的doBegin⽅法中看见那个txObject,如果在⼀个事务内,就会复⽤Connection,所以切换不了数据源

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。