springboot的快速集成多数据源dynamic-datasource-spring-boot-starter为依赖
dynamic-datasource-spring-boot-starter 是一个基于springboot的快速集成多数据源的启动器。
github: https://github.com/baomidou/dynamic-datasource-spring-boot-starter
文档: https://github.com/baomidou/dynamic-datasource-spring-boot-starter/wiki
- 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。
- 支持数据库敏感配置信息 加密(可自定义) ENC()。
- 支持每个数据库独立初始化表结构schema和数据库database。
- 支持无数据源启动,支持懒加载数据源(需要的时候再创建连接)。
- 支持 自定义注解 ,需继承DS(3.2.0+)。
- 提供并简化对Druid,HikariCp,BeeCp,Dbcp2的快速集成。
- 提供对Mybatis-Plus,Quartz,ShardingJdbc,P6sy,Jndi等组件的集成方案。
- 提供 自定义数据源来源 方案(如全从数据库加载)。
- 提供项目启动后 动态增加移除数据源 方案。
- 提供Mybatis环境下的 纯读写分离 方案。
- 提供使用 spel动态参数 解析数据源方案。内置spel,session,header,支持自定义。
- 支持 多层数据源嵌套切换 。(ServiceA >>> ServiceB >>> ServiceC)。
- 提供 基于seata的分布式事务方案 。
- 提供 本地多数据源事务方案。
- 本框架只做 切换数据源 这件核心的事情,并不限制你的具体操作,切换了数据源可以做任何CRUD。
- 配置文件所有以下划线
_分割的数据源 首部 即为组的名称,相同组名称的数据源会放在一个组下。 - 切换数据源可以是组名,也可以是具体数据源名称。组名则切换时采用负载均衡算法切换。
- 默认的数据源名称为 master ,你可以通过
spring.datasource.dynamic.primary修改。 - 代码块里主动切换>方法上的注解优>类上注解(就近原则)。
- DS支持继承抽象类上的DS,支持继承接口上的DS。
- 引入
dynamic-datasource-spring-boot-starter或者dynamic-datasource-spring-boot3-starter。
- spring-boot 1.5.x 2.x.x
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${version}</version>
</dependency>
- spring-boot3及以上
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
<version>${version}</version>
</dependency>
- 配置数据源。
spring: datasource: dynamic: enabled: true #启用动态数据源,默认true primary: master #设置默认的数据源或者数据源组,默认值即为master strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源 grace-destroy: false #是否优雅关闭数据源,默认为false,设置为true时,关闭数据源时如果数据源中还存在活跃连接,至多等待10s后强制关闭 datasource: master: url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置 slave_1: url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver slave_2: url: ENC(xxxxx) # 内置加密,使用请查看详细文档 username: ENC(xxxxx) password: ENC(xxxxx) driver-class-name: com.mysql.jdbc.Driver #......省略 #以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2
多主多从:
spring:
datasource:
dynamic:
datasource:
master_1:
master_2:
slave_1:
slave_2:
slave_3:
纯粹多库:
spring:
datasource:
dynamic:
datasource:
mysql:
oracle:
sqlserver:
postgresql:
h2:
混合配置:
spring:
datasource:
dynamic:
datasource:
master:
slave_1:
slave_2:
oracle_1:
oracle_2:
- 使用 @DS 切换数据源。
@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解。
| 注解 | 结果 |
|---|---|
| 没有@DS | 默认数据源 |
| @DS("dsName") | dsName可以为组名也可以为具体某个库的名称 |
@Service @DS("slave") public class UserServiceImpl implements UserService { @Autowired private JdbcTemplate jdbcTemplate; public List selectAll() { return jdbcTemplate.queryForList("select * from user"); } @Override @DS("slave_1") public List selectByCondition() { return jdbcTemplate.queryForList("select * from user where age >10"); } }
案例:dynamic-datasource+mybatis使用方法
maven
引入
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.1.5</version>
</dependency>
配置好数据源
spring: datasource: dynamic: primary: master1 strict: false datasource: master1: url: jdbc:mysql://127.0.0.1:3306/master1?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8 username: root password: andrew driver-class-name: com.mysql.cj.jdbc.Driver master2: url: jdbc:mysql://127.0.0.1:3306/master2?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8 username: root password: andrew driver-class-name: com.mysql.cj.jdbc.Driver master3: url: jdbc:oracle:thin:@10.132.212.63:1688:TESTDB username: flx password: flx202108 driver-class-name: oracle.jdbc.OracleDriver logging: level: com.xkcoding: debug com.xkcoding.orm.mybatis.mapper: trace server: port: 8080 # servlet: # context-path: /demo mybatis: type-aliases-package: com.orm.mybatis.dsannotation.entity mapper-locations: classpath:mapper/*/*.xml configuration: map-underscore-to-camel-case: true
service层
里面在想要切换数据源的方法上加上@DS注解就行了,也可以加在整个service层上,方法上的注解优先于类上注解
@Service public class UserServiceImpl { @Resource private UserMapper1 userMapper1; @Resource private UserMapper2 userMapper2; @Resource private AsusPoInfoMapper3 asusPoInfoMapper3; @DS("master1") public List<User> findAllUser(){ List<User> list = userMapper1.selectAllUser(); return list; } @DS("master2") public List<User> findAllUser1(){ List<User> list = userMapper2.selectAllUser(); return list; } public User findUserById(Long id){ return userMapper1.selectUserById(id); } @Transactional //与dynamic不同的是,这两个注解可以一起使用会先切换数据源再事务 @DS("master1") //与dynamic不同的是,这两个注解可以一起使用会先切换数据源再事务 public void insertUser1(){ SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String date = simpleDateFormat.format(new Date()); String UUID = java.util.UUID.randomUUID().toString().substring(0,5); User user = User.builder().email("andrew@qq.com"+UUID).name("andrew"+UUID).password("123456"+UUID).phoneNumber("123"+UUID) .lastUpdateTime(date).createTime(date).status(0).salt("password"+UUID).build(); userMapper1.saveUser(user); } @Transactional @DS("master2") public void insertUser2(){ SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String date = simpleDateFormat.format(new Date()); String UUID = java.util.UUID.randomUUID().toString().substring(0,5); User user = User.builder().email("andrew@qq.com"+UUID).name("andrew"+UUID).password("123456"+UUID).phoneNumber("123"+UUID) .lastUpdateTime(date).createTime(date).status(0).salt("password"+UUID).build(); userMapper2.saveUser(user); } public void testTransitional() { ((UserServiceImpl)AopContext.currentProxy()).insertUser1(); ((UserServiceImpl)AopContext.currentProxy()).insertUser2(); ((UserServiceImpl)AopContext.currentProxy()).insertOracle(); } @DS("master3") public List<AsusPoInfo> selectOracle(){ return asusPoInfoMapper3.selectAllAsusPoInfo(); } @DS("master3")//与dynamic不同的是,这两个注解可以一起使用会先切换数据源再事务 @Transactional public void insertOracle(){ AsusPoInfo asusPoInfo = AsusPoInfo.builder().id(java.util.UUID.randomUUID().toString().substring(0,20)) .woNo("andrew").po("123456").poLine("poline").cPo("cpo123456").shipType("Direct").build(); asusPoInfoMapper3.insertAsusPoInfo(asusPoInfo); } }
测试多数据源回滚
package com.orm.mybatis.dsannotation; import com.orm.mybatis.dsannotation.entity.AsusPoInfo; import com.orm.mybatis.dsannotation.entity.User; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import com.orm.mybatis.dsannotation.serviceImpl.UserServiceImpl; import javax.annotation.Resource; import java.util.List; @SpringBootTest class SpringbootMybatisDsannotationDatasourceApplicationTests { //事务测试 @Resource private UserServiceImpl userService; @Test void contextLoads1() { List<AsusPoInfo> list = userService.selectOracle(); System.out.println(list); } @Test void contextLoads2() { List<User> list = userService.findAllUser(); System.out.println(list); } @Test void contextLoads3() { List<User> list = userService.findAllUser(); List<User> list1 = userService.findAllUser1(); List<AsusPoInfo> list2 = userService.selectOracle(); list.addAll(list1); System.out.println(list); System.out.println(list2); } @Test void contextLoads4() { userService.testTransitional(); } }
切换数据源成功,而且事务能回滚,但如果是多数据源事务,只能回滚报错的数据源的事务。
方案的权衡
- 静态多数据源方案优势在于配置简单并且对业务代码的入侵性极小,缺点也显而易见:我们需要在系统中占用一些资源,而这些资源并不是一直需要,一定程度上会造成资源的浪费。如果你需要在一段业务代码中同时使用多个数据源的数据又要去考虑操作的原子性(事务)可以用spring的jta实现事务,那么这种方案无疑会适合你。
- (aop和dynamic)动态数据源(AbstractRoutingDataSource)方案配置上看起来配置会稍微复杂一些,但是很好的符合了“即拿即用,即用即还”的设计原则,我们把多个数据源看成了一个池子,然后进行消费。它的缺点正如上文所暴露的那样:我们往往需要在事务的需求下做出妥协。而且由于需要切换环境上下文,在高并发量的系统上进行资源竞争时容易发生死锁等活跃性问题。我们常用它来进行数据库的“读写分离”,不需要在一段业务中同时操作多个数据源。这种动态形式并不能用spring的jta实现,而且其他实现方式(seata等)虽然可以实现,但配置复杂且实用度不高。
- 如果需要使用事务,一定记得使用分布式事务进行Spring自带事务管理的替换,否则将无法进行一致性控制。
参考:
https://github.com/baomidou/dynamic-datasource
https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611
https://blog.csdn.net/qq_34246965/article/details/123781669

浙公网安备 33010602011771号