0


SpringBoot实现多数据源切换

1. 概述


仓库地址:https://gitee.com/aopmin/multi-datasource-demo

随着项目规模的扩大和业务需求的复杂化,单一数据源已经不能满足实际开发中的需求。在许多情况下,我们需要同时操作多个数据库,或者需要将不同类型的数据存储在不同的数据库中。这时,多数据源场景成为必不可少的解决方案。

市面上常见的多数据源实现方案如下:

  • 方案1:基于Spring框架提供的AbstractRoutingDataSource。- 优点: 简单易用,支持动态切换数据源;适用于少量数据源情况。- 场景:适用于需要动态切换数据源,且数据库较少的情况。- 文档地址:
  • 方案2:使用MP提供的Dynamic-datasource多数据源框架。- 文档地址:https://baomidou.com/guides/dynamic-datasource/#dynamic-datasource
  • 方案3:通过自定义注解在方法或类上指定数据源,实现根据注解切换数据源的功能。- 优点: 灵活性高,能够精确地控制数据源切换;在代码中直观明了。- 场景: 适用于需要在代码层面进行数据源切换,并对数据源切换有精细要求的情况。
  • 方案4:使用动态代理技术,在运行时动态切换数据源,实现多数据源的切换。- 优点: 灵活性高,支持在运行时动态切换数据源;适合对数据源切换的逻辑有特殊需求的情况。- 场景: 适用于需要在运行时动态决定数据源切换策略的情况。

2. 基于SpringBoot的多数据源实现方案


1、执行sql脚本:(分别创建两个数据库,里面都提供一张user表)

  1. -- 创建数据库ds1
  2. CREATEDATABASE `ds1`;-- 使用ds1数据库
  3. USE ds1;-- 创建user
  4. CREATETABLE `user` (
  5. `id` INTPRIMARYKEYAUTO_INCREMENTCOMMENT'主键id',
  6. `username` VARCHAR(50)COMMENT'用户名',
  7. `gender` TINYINT(1)COMMENT '性别:0男,1女'
  8. );-- user表插入数据
  9. INSERTINTO user (username, gender)VALUES('张三',1),('李四',0),('王五',1);-- 创建数据库ds2
  10. CREATEDATABASE `ds2`;-- 使用ds2数据库
  11. USE ds2;-- 创建user
  12. CREATETABLE `user` (
  13. `id` INTPRIMARYKEYAUTO_INCREMENTCOMMENT'主键id',
  14. `username` VARCHAR(50)COMMENT'用户名',
  15. `gender` TINYINT(1)COMMENT '性别:0男,1女'
  16. );-- user表插入数据
  17. INSERTINTO user (username, gender)VALUES('赵六',1),('陈七',0),('宝国',1);

2、创建一个maven工程,向pom.xml中添加依赖:

  1. <!--锁定SpringBoot版本--><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.10</version><relativePath/><!-- lookup parent from repository --></parent><dependencies><!--jdbc起步依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!--test起步依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><!--mysql驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.20</version></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--jdbc起步依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency></dependencies>

3、编写实体类:

  1. packagecn.aopmin.entity;importlombok.*;/**
  2. * 实体类
  3. *
  4. * @author 白豆五
  5. * @since 2024/7/4
  6. */@Data@Builder@NoArgsConstructor@AllArgsConstructor@ToStringpublicclassUser{privateInteger id;privateString username;privateInteger gender;}

4、创建application.yml文件,配置数据源:

  1. spring:#动态数据源配置datasource:ds1:driverClassName: com.mysql.cj.jdbc.Driver
  2. jdbcUrl: jdbc:mysql://localhost:3306/ds1?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=falseusername: root
  3. password:123456ds2:driverClassName: com.mysql.cj.jdbc.Driver
  4. jdbcUrl: jdbc:mysql://localhost:3306/ds2?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=falseusername: root
  5. password:123456logging:level:cn.aopmin: debug

5、编写数据源配置类:

  1. packagecn.aopmin.config;importorg.springframework.beans.factory.annotation.Qualifier;importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.boot.jdbc.DataSourceBuilder;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.jdbc.core.JdbcTemplate;importjavax.sql.DataSource;importjava.util.HashMap;importjava.util.Map;/**
  2. * 数据源配置类
  3. * 配置多数据源和动态数据源
  4. *
  5. * @author 白豆五
  6. * @since 2024/7/4
  7. */@ConfigurationpublicclassDataSourceConfig{//定义数据源1@Bean("ds1")@ConfigurationProperties(prefix ="spring.datasource.ds1")publicDataSourceds1(){returnDataSourceBuilder.create().build();}//定义数据源2@Bean("ds2")@ConfigurationProperties(prefix ="spring.datasource.ds2")publicDataSourceds2(){returnDataSourceBuilder.create().build();}//定义动态数据源@Bean(name ="dataSource")publicDataSourcedynamicDataSource(@Qualifier("ds1")DataSource ds1,@Qualifier("ds2")DataSource ds2){//1.定义数据源mapMap<Object,Object> targetDataSources =newHashMap<>();
  8. targetDataSources.put("ds1", ds1);
  9. targetDataSources.put("ds2", ds2);//2.实例化自定义的DynamicDataSource对象, 并设置数据源mapDynamicDataSource dynamicDataSource =newDynamicDataSource();
  10. dynamicDataSource.setTargetDataSources(targetDataSources);//3.设置默认数据源,未匹配上则使用默认数据源
  11. dynamicDataSource.setDefaultTargetDataSource(ds1);return dynamicDataSource;}// 通过JdbcTemplate @BeanpublicJdbcTemplatejdbcTemplate(@Qualifier("dataSource")DataSource ds){returnnewJdbcTemplate(ds);}}

6、创建DynamicDataSource动态数据类:

  1. packagecn.aopmin.config;importcn.aopmin.common.DataSourceContextHolder;importorg.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/**
  2. * AbstractRoutingDataSource(抽象的数据源路由器) 的基本原理是, 它维护了一个数据源的集合,每个数据源都有唯一的一个标识符
  3. * 当应用程序需要访问数据库的时候,AbstractRoutingDataSource会根据某种匹配规则(例如请求参数、用户身份等)来选择一个合适的数据源,
  4. * 并将请求转发给这个数据源。
  5. */publicclassDynamicDataSourceextendsAbstractRoutingDataSource{/**
  6. * 获取数据源名称
  7. * @return
  8. */@OverrideprotectedObjectdetermineCurrentLookupKey(){returnDataSourceContextHolder.getDataSource();}}

7、定义一个ThreadLocal工具类:

  1. packagecn.aopmin.common;/**
  2. * 使用ThreadLocal保存数据源名称
  3. *
  4. * @author 白豆五
  5. * @since 2024/7/4
  6. */publicclassDataSourceContextHolder{privatestaticfinalThreadLocal<String> contextHolder =newThreadLocal<>();// 将数据源名称绑定到当前线程上publicstaticvoidsetDataSource(String dataSourceName){
  7. contextHolder.set(dataSourceName);}// 获取当前线程上的数据源名称publicstaticStringgetDataSource(){return contextHolder.get();}// 清除数据源名称publicstaticvoidclearDataSource(){
  8. contextHolder.remove();}}

8、创建启动类

  1. packagecn.aopmin;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;/**
  2. * 启动类
  3. *
  4. * @author 白豆五
  5. * @since 2024/7/3
  6. */@SpringBootApplicationpublicclassDemo01Application{publicstaticvoidmain(String[] args){SpringApplication.run(Demo01Application.class, args);}}

9、创建UserService:

  1. packagecn.aopmin.service;importcn.aopmin.common.DataSourceContextHolder;importcn.aopmin.entity.User;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.jdbc.core.JdbcTemplate;importorg.springframework.stereotype.Service;/**
  2. * @author 白豆五
  3. * @since 2024/7/4
  4. */@ServicepublicclassUserService{@AutowiredprivateJdbcTemplate jdbcTemplate;publicvoidinsertDs1(User user){try{// todo:自定义注解+SpringAop实现数据源的切换DataSourceContextHolder.setDataSource("ds1");String sql ="insert into user(username,gender) values(?,?)";
  5. jdbcTemplate.update(sql,user.getUsername(), user.getGender());}finally{DataSourceContextHolder.clearDataSource();}}publicvoidinsertDs2(User user){try{DataSourceContextHolder.setDataSource("ds2");String sql ="insert into user(username,gender) values(?,?)";
  6. jdbcTemplate.update(sql,user.getUsername(), user.getGender());}finally{DataSourceContextHolder.clearDataSource();}}}

10、编写测试:

  1. packagecn.aopmin.service;importcn.aopmin.Demo01Application;importcn.aopmin.entity.User;importorg.junit.jupiter.api.Test;importorg.springframework.boot.test.context.SpringBootTest;importjavax.annotation.Resource;@SpringBootTest(classes ={Demo01Application.class})publicclassUserServiceTest{@ResourceprivateUserService userService;@TestpublicvoidtestInsertDs1(){User user =newUser();
  2. user.setUsername("jack");
  3. user.setGender(0);
  4. user.setGender(1);
  5. userService.insertDs1(user);}@TestpublicvoidtestInsertDs2(){User user =newUser();
  6. user.setUsername("rose");
  7. user.setGender(1);
  8. userService.insertDs2(user);}}

最终效果:

在这里插入图片描述

3. 基于Dynamic-datasource实现方案


mp文档:https://baomidou.com/guides/dynamic-datasource/#_top

1、创建SpringBoot工程,引入Dynamic-datasource依赖:

  1. <dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.5.2</version></dependency>

2、配置数据源:

  1. spring:#多数据源配置datasource:dynamic:primary: master #设置默认数据源strict:false#是否严格检查动态数据源提供的数据库名datasource:#数据源1master:url: jdbc:mysql:///ds1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghaiusername: root
  2. password:123456driver-class-name: com.mysql.cj.jdbc.Driver
  3. #数据源2slave1:url: jdbc:mysql:///ds2?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghaiusername: root
  4. password:123456driver-class-name: com.mysql.cj.jdbc.Driver

3、实体类:

  1. packagecn.aopmin.entity;importlombok.AllArgsConstructor;importlombok.Builder;importlombok.Data;importlombok.NoArgsConstructor;/**
  2. * 实体类
  3. *
  4. * @author 白豆五
  5. * @since 2024/7/4
  6. */@Data@Builder@NoArgsConstructor@AllArgsConstructorpublicclassUser{privateInteger id;privateString username;privateInteger gender;}

4、业务类:

  1. packagecn.aopmin.service;importcn.aopmin.entity.User;importcom.baomidou.dynamic.datasource.annotation.DS;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.jdbc.core.JdbcTemplate;importorg.springframework.stereotype.Service;/**
  2. * 通过@DS注解切换数据源
  3. * @author 白豆五
  4. * @since 2024/7/4
  5. */@Service// @DS("master") //不加@DS注解,会使用默认数据源publicclassUserService{@AutowiredprivateJdbcTemplate jdbcTemplate;publicvoidinsertDs1(User user){String sql ="insert into user(username,gender) values(?,?)";
  6. jdbcTemplate.update(sql,user.getUsername(), user.getGender());}@DS("slave1")publicvoidinsertDs2(User user){String sql ="insert into user(username,gender) values(?,?)";
  7. jdbcTemplate.update(sql,user.getUsername(), user.getGender());}}

4、测试类:

  1. packagecn.aopmin.service;importcn.aopmin.entity.User;importorg.junit.jupiter.api.Test;importorg.springframework.boot.test.context.SpringBootTest;importjavax.annotation.Resource;@SpringBootTestpublicclassUserServiceTest2{@ResourceprivateUserService userService;@TestpublicvoidtestInsertDs1(){User user =newUser();
  2. user.setUsername("jack");
  3. user.setGender(0);
  4. user.setGender(1);
  5. userService.insertDs1(user);}@TestpublicvoidtestInsertDs2(){User user =newUser();
  6. user.setUsername("rose");
  7. user.setGender(1);
  8. userService.insertDs2(user);}}

本文转载自: https://blog.csdn.net/qq_46921028/article/details/140168371
版权归原作者 白豆五 所有, 如有侵权,请联系我们删除。

“SpringBoot实现多数据源切换”的评论:

还没有评论