一.需求背景
项目需要实现在多个数据源之间读写数据,例如在 A 数据源和 B 数据源读取数据,然后在 C 数据源写入数据 或者 部分业务数据从 A 数据源中读取、部分从B数据源中读取诸如此类需求。本文将简单模拟在SpringBoot项目中实现不同数据源之间读取数据。
二.技术概述
dynamic-datasource
是一个开源的 SpringBoot 多数据源启动器,提供了丰富的功能,**使用者只需完成基本的数据源配置,配合使用
@DS
注解即可轻松实现多数据源之间的切换**。其支持 Jdk 1.7+, SpringBoot 1.5.x 2.x.x 3.x.x。JPA用户不建议使用,JPA自带事务,无法连续切库。
以下特性和约定摘录自官方文档:dynamic-datasource
- 特性- 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。- 支持数据库敏感配置信息 加密(可自定义) 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的分布式事务方案 。- 提供 本地多数据源事务方案。
- 约定1. 本框架只做 切换数据源 这件核心的事情,并不限制你的具体操作,切换了数据源可以做任何CRUD。2. 配置文件所有以下划线
_
分割的数据源 首部 即为组的名称,相同组名称的数据源会放在一个组下。3. 切换数据源可以是组名,也可以是具体数据源名称。组名则切换时采用负载均衡算法切换。、4. 默认的数据源名称为 master ,你可以通过spring.datasource.dynamic.primary
修改。5. 代码块里主动切换 > 方法上的注解优 > 类上注解(就近原则)6. DS支持继承抽象类上的DS,支持继承接口上的DS。
注:官网文档只有基础篇可免费查阅,其他内容查阅需付费
29.9¥
,因此如果基础篇所讲解功能无法满足需求(业务场景比较复杂)且不愿付费请勿使用本框架!
三.编码实现
业务说明:从A数据源查询出学生数据同时从B数据源查询出教师数据。
技术选型:SpringBoot 2.7.6 + MyBatis-Plus 3.5.4 + MySQL 8.0
1)引入Maven坐标
<dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.5.2</version></dependency>
2)创建模拟数据库表,
# A 数据源createDATABASE testA;use testA;CREATETABLE student(
id int,
username VARCHAR(12));# B 数据源createDATABASE testB;use testB;CREATETABLE teacher(
id int,
username VARCHAR(12));
3)在yml文件中配置数据源
spring:datasource:dynamic:primary: testA #设置默认的数据源或者数据源组,默认值即为masterstrict:false#严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源datasource:testA:driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/testA
username: root
password: root
testB:driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/testB
username: root
password: root
4)可以用MyBatis-X插件快速生成项目骨架。
5)使用
@DS
注解在mapper文件中指定数据源。注意,数据源名称需要与yml配置中的一致。
// 指定学生表的数据源为testA@DS("testA")publicinterfaceStudentMapperextendsBaseMapper<Student>{}// 指定教师表的数据源为testB@DS("testB")publicinterfaceTeacherMapperextendsBaseMapper<Teacher>{}
5)创建测试类进行测试
@SpringBootTestpublicclassMultipleDataSourceTest{@ResourceprivateStudentService studentService;@ResourceprivateTeacherService teacherService;@TestvoidtestSelect(){System.out.println("====== 学生表数据 ======");for(Student student : studentService.list()){System.out.println(">>> "+ student);}System.out.println("====== 教师表数据 ======");for(Teacher teacher : teacherService.list()){System.out.println(">>> "+ teacher);}}}
6)相关执行日志查看,可以通过日志看到已经成功加载两个数据源并且指定默认数据源为[testA],同时我们所查询的数据也能正常出来。
INFO312---[main]com.zaxxer.hikari.HikariDataSource: testA -Starting...INFO312---[main]com.zaxxer.hikari.HikariDataSource: testA -Start completed.INFO312---[main]com.zaxxer.hikari.HikariDataSource: testB -Starting...INFO312---[main]com.zaxxer.hikari.HikariDataSource: testB -Start completed.INFO312---[main]c.b.d.d.DynamicRoutingDataSource: dynamic-datasource - add a datasource named [testB] success
INFO312---[main]c.b.d.d.DynamicRoutingDataSource: dynamic-datasource - add a datasource named [testA] success
INFO312---[main]c.b.d.d.DynamicRoutingDataSource: dynamic-datasource initial loaded [2] datasource,primary datasource named [testA]====== 学生表数据 ======>>>Student(id=1, username=学生1)>>>Student(id=2, username=学生2)>>>Student(id=3, username=学生3)>>>Student(id=4, username=学生4)>>>Student(id=5, username=学生5)====== 教师表数据 ======>>>Teacher(id=1, username=教师1)>>>Teacher(id=2, username=教师2)>>>Teacher(id=3, username=教师3)>>>Teacher(id=4, username=教师4)>>>Teacher(id=5, username=教师5)INFO312---[ionShutdownHook]c.b.d.d.DynamicRoutingDataSource: dynamic-datasource start closing ....INFO312---[ionShutdownHook]com.zaxxer.hikari.HikariDataSource: testB -Shutdown initiated...INFO312---[ionShutdownHook]com.zaxxer.hikari.HikariDataSource: testB -Shutdown completed.INFO312---[ionShutdownHook]com.zaxxer.hikari.HikariDataSource: testA -Shutdown initiated...INFO312---[ionShutdownHook]com.zaxxer.hikari.HikariDataSource: testA -Shutdown completed.INFO312---[ionShutdownHook]c.b.d.d.DynamicRoutingDataSource: dynamic-datasource all closed success,bye
四.补充说明
以上示例可以完成基本的多数据源切换需求,对于一些复杂场景,由于官方文档付费,请根据需要使用。例如:
- 在需要切换数据源的代码最外层加上了事务注解
@Transactional
将会导致数据源切换失效,上述代码加上注解运行将报错(由于无法切换数据源,将使用默认数据源,如果默认数据源中没有对应的表将报错),在网上检索发现通过添加官方提供的@DSTransactional
即可实现事务下数据源切换:@Test@TransactionalvoidtestSelect(){System.out.println("====== 学生表数据 ======");for(Student student : studentService.list()){System.out.println(">>> "+ student);}System.out.println("====== 教师表数据 ======");for(Teacher teacher : teacherService.list()){System.out.println(">>> "+ teacher);}}
- 在多线程情况下,如果使用编码方式实现数据源切换也将导致问题,需要进行一些其他处理。
最后,补充说明想表达的由于官网文档是付费的,不付费的话许多问题只能搜索寻找解决方法比较麻烦,因此,请根据自身业务情况选择使用本框架或其他框架。
版权归原作者 观止study 所有, 如有侵权,请联系我们删除。