目录

  • 前言
  • 项目搭建
    • 主要工具版本
    • 数据准备
    • `pom.xml` 依赖
    • `application.properties` 配置文件
    • 主从数据源的配置类
      • 主数据源
      • 从数据源
    • 项目目录结构
    • 实体类
    • `dao` 层
    • `service` 层
    • `Controller` 层
    • 测试
      • 主数据源测试
      • 从数据源测试

前言

随着业务的不断增加,我们的系统会越来越庞大。显然单个数据库已经承受不了高并发带来的压力。一个项目使用多个数据库(无论是主从复制- - 读写分离还是分布式数据库结构)的重要性变得越来越明显。整合多数据源有两种方法:分包 和 AOP

  • 分包:分包主要是根据业务划分
  • AOP:实际上就是通过 AOP 进行拦截,不同的注解里面的值,指向不同的数据源

项目搭建

主要工具版本

  • SpringBoot2.0.6.RELEASE
  • JDK1.8
  • MySQL5.7

数据准备

创建 test1test2 两个库,分别建表 emp(数据不相同)

  • test1 作为主库,test2 作为从库
  • 主库用于写操作,从库用于读操作
CREATE TABLE `emp` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAULT NULL,`age` int(11) DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

test1 库中的表 emp 数据如下


test2 库中的表 emp 数据如下

pom.xml 依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<!--mysql 驱动-->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.38</version>
</dependency>
<!-- druid 数据库连接池 -->
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version>
</dependency>
<!-- mybatis -->
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.2</version>
</dependency>
<!--lombok 插件-->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version><scope>provided</scope>
</dependency>
<!--api接口文档可视化 swagger-->
<dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version>
</dependency>
<dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>2.9.2</version>
</dependency>

application.properties 配置文件

server.port=8080#主数据源test1配置
spring.datasource.test1.url=jdbc:mysql://127.0.0.1:3306/test1?characterEncoding=utf8&useSSL=false&autoReconnect=true&serverTimezone=UTC
spring.datasource.test1.username=root
spring.datasource.test1.password=123456
spring.datasource.test1.druid.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.test1.type=com.alibaba.druid.pool.DruidDataSource#主数据源test1连接池配置
spring.datasource.druid.test1.initial-size=3
spring.datasource.druid.test1.min-idle=3
spring.datasource.druid.test1.max-active=10
spring.datasource.druid.test1.max-wait=60000#从数据源test2配置
spring.datasource.test2.url=jdbc:mysql://127.0.0.1:3306/test2?characterEncoding=utf8&useSSL=false&autoReconnect=true&serverTimezone=UTC
spring.datasource.test2.username=root
spring.datasource.test2.password=123456
spring.datasource.test2.druid.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.test2.type=com.alibaba.druid.pool.DruidDataSource#从数据源test2连接池配置
spring.datasource.druid.test2.initial-size=3
spring.datasource.druid.test2.min-idle=3
spring.datasource.druid.test2.max-active=10
spring.datasource.druid.test2.max-wait=60000mybatis.configuration.cache-enabled=true
#开启驼峰命名
mybatis.configuration.map-underscore-to-camel-case=true
#打印 SQL 语句
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
  • url:此处使用的是 url,不是 jdbc-url。因为下面的配置类中使用的是 DruidDataSourceBuilder 构建数据源,而不是使用 DataSourceBuilder 构建数据源
  • 使用 DruidDataSourceBuilder 构建数据源,使用的数据库连接池是 druid
  • 使用 DataSourceBuilder 构建数据源,使用的数据库连接池是 springboot 默认的 HikariCP
  • SpringBoot 的启动类中也并没有 exclude = DataSourceAutoConfiguration.class

主从数据源的配置类

主数据源

@Configuration
/*扫描mybatis的接口位置*/
@MapperScan(basePackages = "org.example.test1.mapper", sqlSessionFactoryRef = "test1SqlSessionFactory")
public class DataSourceConfig1 {@Primary /*表示这个数据源是默认主数据源*/@Bean(name = "test1DataSource")@ConfigurationProperties(prefix = "spring.datasource.test1")public DataSource getDateSource1() {return DruidDataSourceBuilder.create().build();}@Primary@Bean(name = "test1SqlSessionFactory")public SqlSessionFactory test1SqlSessionFactory(@Qualifier("test1DataSource") DataSource datasource) throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(datasource);bean.setMapperLocations(/*设置 mybatis 的 xml 所在位置*/new PathMatchingResourcePatternResolver().getResources("classpath:org/example/test1/mapper/*.xml"));return bean.getObject();}@Primary@Bean("test1SqlSessionTemplate")public SqlSessionTemplate test1sqlsessiontemplate(@Qualifier("test1SqlSessionFactory") SqlSessionFactory sessionfactory) {return new SqlSessionTemplate(sessionfactory);}
}
  • @Primary:这个注解必须要加,因为不加的话 spring 将分不清楚哪个为主数据源(默认数据源)

从数据源

@Configuration
@MapperScan(basePackages = "org.example.test2.mapper", sqlSessionFactoryRef = "test2SqlSessionFactory")
public class DataSourceConfig2 {@Bean(name = "test2DataSource")@ConfigurationProperties(prefix = "spring.datasource.test2")public DataSource getDateSource2() {return DruidDataSourceBuilder.create().build();}@Bean(name = "test2SqlSessionFactory")public SqlSessionFactory test2SqlSessionFactory(@Qualifier("test2DataSource") DataSource datasource)throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(datasource);bean.setMapperLocations(/*设置 mybatis 的 xml 所在位置*/new PathMatchingResourcePatternResolver().getResources("classpath:org/example/test2/mapper/*.xml"));return bean.getObject();}@Bean("test2SqlSessionTemplate")public SqlSessionTemplate test2sqlsessiontemplate(@Qualifier("test2SqlSessionFactory") SqlSessionFactory sessionfactory) {return new SqlSessionTemplate(sessionfactory);}
}

项目目录结构

实体类

@Data
@ApiModel
public class Emp implements Serializable {@ApiModelProperty(value = "员工id", example = "0")private Integer id;@ApiModelProperty(value = "员工姓名")@NotBlank(message = "员工姓名不能为空")private String name;@ApiModelProperty(value = "员工年龄", example = "0")@NotNull(message = "员工年龄不能为空")private Integer age;
}

dao

public interface EmpMapper1 {int deleteByPrimaryKey(Integer id);int insert(Emp record);int insertSelective(Emp record);Emp selectByPrimaryKey(Integer id);int updateByPrimaryKeySelective(Emp record);int updateByPrimaryKey(Emp record);
}public interface EmpMapper2 {int deleteByPrimaryKey(Integer id);int insert(Emp record);int insertSelective(Emp record);Emp selectByPrimaryKey(Integer id);int updateByPrimaryKeySelective(Emp record);int updateByPrimaryKey(Emp record);List<Emp> findAll();
}
  • 此处不需要 @Mapper 注解,在主从数据源配置类中通过 @MapperScan 已经将其分别扫描进 Spring 中了

service

Service 层中根据不同的业务注入不同的 dao 层,这里不再赘述

Controller

@Controller
@RequestMapping(path = "/emp")
@Api(tags = "人员管理相关接口")
public class EmpController {@Autowiredprivate EmpService empService;/*** 主数据源test1负责写入数据** @Author: CJ* @Date: 2021-9-2 21:24* @param emp:* @return: org.example.util.ResultMap*/@PostMapping(path = "/addEmp")@ResponseBody@ApiOperation(value = "添加人员的接口")public ResultMap addEmp(@Valid Emp emp, @NotNull BindingResult bindingResult) {if (bindingResult.hasErrors()) {String defaultMessage = Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage();return new ResultMap().fail().message(defaultMessage);}int i = empService.addEmp(emp);if (i < 0) {return new ResultMap().fail().message("添加失败");}return new ResultMap().success().message("添加成功");}/*** 从数据源test2负责读取数据** @Author: CJ* @Date: 2021-9-2 21:28* @param id:* @return: org.example.util.ResultMap*/@GetMapping(path = "/findEmp")@ResponseBody@ApiOperation(value = "查询特定人员的接口")@ApiImplicitParam(name = "id", value = "员工id", required = true)public ResultMap findEmp(@RequestParam(name = "id") Integer id) {Emp emp = empService.selectEmpById(id);if (StringUtils.isEmpty(emp)) {return new ResultMap().fail().message("查询失败");}return new ResultMap().success().data(emp).message("查询成功");}/*** 从数据源test2负责读取数据** @Author: CJ* @Date: 2021-9-2 21:28* @return: org.example.util.ResultMap*/@GetMapping("/selectAllEmp")@ResponseBody@ApiOperation(value = "查询所有人员的接口")public ResultMap selectAllEmp() {List<Emp> emps = empService.selectAll();if (CollectionUtils.isEmpty(emps)) {return new ResultMap().fail().message("查询失败");}return new ResultMap().success().data(emps).message("查询成功");}
}

测试

启动 springboot 项目,浏览器中输入:http://localhost:8080/swagger-ui.html


SpringBoot 整合 Swagger 文章:https://blog.csdn.net/weixin_38192427/article/details/120060508

主数据源测试

向主数据源写入数据,参数如下


响应结果如下


主数据库 test1 新增数据如下


使用的数据库连接池是 druid

从数据源测试


test2 从数据库的 emp 表数据


test1 主数据库的 emp 表数据


详细代码参见:https://gitee.com/chaojiangcj/springboot-multipledatasources

SpringBoot配置多数据源(动态切换)相关推荐

  1. Proxool配置多数据源动态切换

    2019独角兽企业重金招聘Python工程师标准>>> 前段时间遇到多数据源动态切换问题,总结一下,做个记录,以备后续之需! 首先附上proxool连接池的配置方法:http://3 ...

  2. SpringBoot+MybatisPlus多数据源动态切换

    公司某项目做大屏展示,但数据来源自7个不同的数据库,需要涉及跨库查询,要求. 本项目采用SpringBoot+MybatisPlus做服务端提供RESTful接口,前后端分离开发,总结一下项目中实现的 ...

  3. Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法

    原文地址:http://www.cnblogs.com/hoojo/p/Spring_Hibernate_MyBatis_MultipleDataSource_switchDataSource.htm ...

  4. springboot多数据源动态切换和自定义mybatis分页插件

    1.配置多数据源 增加druid依赖 完整pom文件 数据源配置文件 route.datasource.driver-class-name= com.mysql.jdbc.Driver route.d ...

  5. Spring3 整合MyBatis3 配置多数据源 动态选择SqlSessionFactory

    一.摘要 上两篇文章分别介绍了Spring3.3 整合 Hibernate3.MyBatis3.2 配置多数据源/动态切换数据源 方法 和 Spring3 整合Hibernate3.5 动态切换Ses ...

  6. SpringBoot+AOP实现多数据源动态切换

    SpringBoot+AOP实现多数据源动态切换 背景 设计总体思路 步骤 背景 系统后端需要访问多个数据库,现有的数据库连接配置写入配置文件中.后端需要从一个数据库的配置表里动态的读取其它mysql ...

  7. Spring-Boot + AOP实现多数据源动态切换

    2019独角兽企业重金招聘Python工程师标准>>> 最近在做保证金余额查询优化,在项目启动时候需要把余额全量加载到本地缓存,因为需要全量查询所有骑手的保证金余额,为了不影响主数据 ...

  8. springboot使用mybatis多数据源动态切换的实现

    需求:项目使用了读写分离,或者数据进行了分库处理,我们希望在操作不同的数据库的时候,我们的程序能够动态的切换到相应的数据库,执行相关的操作. 首先,你需要一个能够正常运行的springboot项目,配 ...

  9. Spring+Mybatis多数据源配置(四)——AbstractRoutingDataSource实现数据源动态切换

    欢迎支持笔者新作:<深入理解Kafka:核心设计与实践原理>和<RabbitMQ实战指南>,同时欢迎关注笔者的微信公众号:朱小厮的博客. 欢迎跳转到本文的原文链接:https: ...

  10. springboot配置Druid数据源

    springboot配置druid数据源 Author:SimpleWu springboot整合篇 前言 对于数据访问层,无论是Sql还是NoSql,SpringBoot默认采用整合SpringDa ...

最新文章

  1. nagios视频教程【原创】
  2. 规律的更新状态是一个好习惯
  3. Android AsyncTask 深度理解、简单封装、任务队列分析、自定义线程池
  4. 目标检测之空间变形网络(STN)
  5. 黑马安卓74期Android基础(0)
  6. i219v微星 驱动_MSI微星
  7. [统计学理论基础] 统计方法—F检验
  8. 【自用】R语言处理GEO转录组数据记录
  9. React+Antd+TypeScript 开发规范
  10. 284、超详细的光纤熔纤、盘纤教程,值得收藏
  11. 解决error ‘XXX‘ is not defined no-undef且项目没有eslintrc.js文件问题
  12. linux 命令 root用户把某个文件权限给到普通用户
  13. ansys_lsdyna输出.k文件lsprepost输出部件加速度
  14. 杰理之VM 概述【篇】
  15. 编译librtmp for Android
  16. PostgreSQL数据库之国际化语言支持学习总结
  17. STM32F767/429-CAN通信实验
  18. java对象克隆效率_fastclone
  19. idea 重新安装流程
  20. [渝粤教育] 西南科技大学 电工学 在线考试复习资料

热门文章

  1. 容器技术Docker K8s 33 04-容器服务ACK基础与进阶-06-集群管理
  2. HDF5 library version mismatched error
  3. python3 协程_Python3 异步神器-协程(Coroutine)
  4. 译林 五年级上 单词_译林版小学英语五上Unit 4 HobbiesStory time公开课优质课件教案视频教案...
  5. Boost组件lexical_cast
  6. python列表删除会出现一个错误 list index out of range
  7. vue 背景弹出禁止滚动_vue-蒙层弹窗里的内容滚动。外层大页面禁止滚动
  8. Mysql更换版本的操作,非常详细(包括数据备份,卸载,安装,还原)
  9. matlab解六元一次方程,如何用MATLAB编写六元一次方程组
  10. linux服务之FTP服务篇