• mybatisplus快速实现动态数据源切换


    1.背景
     

      通常一个系统只需要连接一个数据库就可以了。但是在企业应用的开发中往往会和其他子系统交互,特别是对于一些数据实时性要求比较高的数据,我们就需要做实时连接查询,而不是做同步。这个时候就需要用到多数据源。
     

      举个简单的例子某企业要做订单网上订单系统这里面就可以涉及到多个子系统的连接,比如:产品主数据的数据源,项目管理系统的数据源(项目可以产品订单)等多个不同数据库类似的数据源,他们可能是ORACLE,SQL SERVER,MYSQL等多种混合数据源。
     

      2.多数据源概述
     

      基于以上的背景,就会选择使用多个数据源,一个数据源用于读一个数据源用于写。或者不同的数据源混合使用。他的基本思想其实就是AOP。我们可以通过AOP的思想实现动态数据源切换,通过这个AOP思想可适用于多种场景、纯粹多库、读写分离、一主多从、混合模式等。

      动态数据源能进行自动切换的核心就是spring底层的AbstractRoutingDataSource进行数据源的路由,只要继承了这个类均可看作是一个数据源的实现。主要实现方法是determineCurrentLookupkey,该方法只需要返回数据源实例名称。
     

      3.mybatisplus多数据源
     

      我们在项目中用mybatisplus的使用用得比较多,这个动态数据源切换需要实现的话,比较麻烦,如果有现成的框架使用则最好不过了。恰好mybatiplus就能实现。文档地址如下:
     

      ```properties

      https://baomidou.com/pages/a61e1b/#%E6%96%87%E6%A1%A3-documentation

      https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611

      ```

      4.使用
     

      4.1介绍
     

      dynamic-datasource-spring-boot-starter是一个基于springboot的快速集成多数据源的启动器。
     

      特性:

      -支持**数据源分组**,适用于多种场景纯粹多库读写分离一主多从混合模式。

      -支持数据库敏感配置信息**加密**ENC

      -支持每个数据库独立初始化表结构schema和数据库database。

      -支持无数据源启动,支持懒加载数据源(需要的时候再创建连接)。

      -支持**自定义注解**,需继承DS3.2.0+。

      -提供并简化对Druid,HikariCp,BeeCp,Dbcp2的快速集成。

      -提供对Mybatis-Plus,Quartz,ShardingJdbc,P6sy,Jndi等组件的集成方案。

      -提供**自定义数据源来源**方案(如全从数据库加载)。

      -提供项目启动后**动态增加移除数据源**方案。

      -提供Mybatis环境下的**纯读写分离**方案。

      -提供使用**spel动态参数**解析数据源方案。内置spel,session,header,支持自定义。

      -支持**多层数据源嵌套切换**。(ServiceA>>>ServiceB>>>ServiceC)。

      -提供**基于seata的分布式事务方案。

      -提供**本地多数据源事务方案。**
     

      4.2 约定
     

      1.本框架只做**切换数据源**这件核心的事情,并**不限制你的具体操作**,切换了数据源可以做任何CRUD。

      2.配置文件所有以下划线`_`分割的数据源**首部**即为组的名称,相同组名称的数据源会放在一个组下。

      3.切换数据源可以是组名,也可以是具体数据源名称。组名则切换时采用负载均衡算法切换。

      4.默认的数据源名称为**master**,你可以通过`spring.datasource.dynamic.primary`修改。

      5.方法上的注解优先于类上注解。

      6.DS支持继承抽象类上的DS,暂不支持继承接口上的DS。
     

      4.3 使用
     

      4.3.1准备数据库

     docker run --name mysq -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 -d mysql:5.7

      创建2个数据库

    1. create database test1;
    2. create database test2;
    3. use test2;
    4. -- auto-generated definition
    5. create table tb_user
    6. (
    7. id int auto_increment
    8. primary key,
    9. name varchar(200) null
    10. );
    11. insert into tb_user values(1,"wangwu");
    12. use test1;
    13. -- auto-generated definition
    14. create table tb_user
    15. (
    16. id int auto_increment
    17. primary key,
    18. name varchar(200) null
    19. );
    20. insert into tb_user values(1,"zhangsan");

      一个作为主库一个作为从库。
     

      4.3.2 springboot创建工程
     

      添加依赖:

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    5. <modelVersion>4.0.0</modelVersion>
    6. <groupId>com.itheima</groupId>
    7. <artifactId>dynamic</artifactId>
    8. <version>1.0-SNAPSHOT</version>
    9. <parent>
    10. <groupId>org.springframework.boot</groupId>
    11. <artifactId>spring-boot-starter-parent</artifactId>
    12. <version>2.3.9.RELEASE</version>
    13. <relativePath/> <!-- lookup parent from repository -->
    14. </parent>
    15. <properties>
    16. <java.version>1.8</java.version>
    17. </properties>
    18. <dependencies>
    19. <dependency>
    20. <groupId>org.springframework.boot</groupId>
    21. <artifactId>spring-boot-starter-web</artifactId>
    22. </dependency>
    23. <dependency>
    24. <groupId>org.projectlombok</groupId>
    25. <artifactId>lombok</artifactId>
    26. <version>1.18.16</version>
    27. </dependency>
    28. <dependency>
    29. <groupId>com.baomidou</groupId>
    30. <artifactId>mybatis-plus-boot-starter</artifactId>
    31. <version>3.4.0</version>
    32. </dependency>
    33. <dependency>
    34. <groupId>com.baomidou</groupId>
    35. <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    36. <version>3.4.0</version>
    37. </dependency>
    38. <dependency>
    39. <groupId>mysql</groupId>
    40. <artifactId>mysql-connector-java</artifactId>
    41. </dependency>
    42. <dependency>
    43. <groupId>com.alibaba</groupId>
    44. <artifactId>druid-spring-boot-starter</artifactId>
    45. <version>1.1.21</version>
    46. </dependency>
    47. </dependencies>
    48. </project>

      启动类:

    1. package com.itheima;
    2. import org.springframework.boot.SpringApplication;
    3. import org.springframework.boot.autoconfigure.SpringBootApplication;
    4. @SpringBootApplication
    5. public class DynamicApplication {
    6. public static void main(String[] args) {
    7. SpringApplication.run(DynamicApplication.class,args);
    8. }
    9. }

      4.3.3配置yml

    1. spring:
    2. datasource:
    3. dynamic:
    4. primary: master #设置默认的数据源或者数据源组,默认值即为master
    5. strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
    6. datasource:
    7. master:
    8. url: jdbc:mysql://192.168.211.253:3306/test1
    9. username: root
    10. password: 123456
    11. driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
    12. slave_1:
    13. url: jdbc:mysql://192.168.211.253:3306/test2
    14. username: root
    15. password: 123456
    16. driver-class-name: com.mysql.jdbc.Driver
    17. #......省略
    18. #以上会配置一个默认库master,一个组slave下有两个子库slave_1

      4.3.4使用注解来切换数据源
     

      使用**DS**切换数据源,使用方式如下:
     

      **DS**可以注解在方法上或类上,**同时存在就近原则方法上注解优先于类上注解**。

      |注解|结果|

      |-------------|----------------------------------------|

      |没有DS|默认数据源|

      |DS

    "dsName"

    |dsName可以为组名也可以为具体某个库的名称|
     

      例如:

    1. @Service
    2. @DS("slave")
    3. public class UserServiceImpl implements UserService {
    4. @Autowired
    5. private JdbcTemplate jdbcTemplate;
    6. public List selectAll() {
    7. return jdbcTemplate.queryForList("select * from user");
    8. }
    9. @Override
    10. @DS("slave_1")
    11. public List selectByCondition() {
    12. return jdbcTemplate.queryForList("select * from user where age >10");
    13. }
    14. }

      4.3.5创建CSD

      po:

    1. package com.itheima.po;
    2. import com.baomidou.mybatisplus.annotation.IdType;
    3. import com.baomidou.mybatisplus.annotation.TableField;
    4. import com.baomidou.mybatisplus.annotation.TableId;
    5. import com.baomidou.mybatisplus.annotation.TableName;
    6. import lombok.Data;
    7. @Data
    8. @TableName("tb_user")
    9. public class User {
    10. @TableId(type = IdType.AUTO)
    11. private Integer id;
    12. @TableField("name")
    13. private String name;
    14. }

      controller

    1. package com.itheima.controller;
    2. import com.itheima.po.User;
    3. import com.itheima.service.UserService;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.boot.autoconfigure.AutoConfigureOrder;
    6. import org.springframework.web.bind.annotation.GetMapping;
    7. import org.springframework.web.bind.annotation.PathVariable;
    8. import org.springframework.web.bind.annotation.RequestMapping;
    9. import org.springframework.web.bind.annotation.RestController;
    10. @RestController
    11. @RequestMapping("/user")
    12. public class UserController {
    13. @Autowired
    14. private UserService userService;
    15. @GetMapping("/{id}")
    16. public User get(@PathVariable(name="id")Integer id){
    17. return userService.getById(id);
    18. }
    19. }

      service

      ```

      public interface UserService{

      User getById

    Integer id

    ;

      }

      ```

    1. package com.itheima.service.impl;
    2. import com.baomidou.dynamic.datasource.annotation.DS;
    3. import com.itheima.dao.UserDao;
    4. import com.itheima.po.User;
    5. import com.itheima.service.UserService;
    6. import org.springframework.beans.factory.annotation.Autowired;
    7. import org.springframework.stereotype.Service;
    8. @Service
    9. @DS("slave_1")
    10. public class UserServiceImpl implements UserService {
    11. @Autowired
    12. private UserDao userDao;
    13. @Override
    14. public User getById(Integer id) {
    15. return userDao.selectById(id);
    16. }
    17. }

      注意:如上:DS注解用于指定使用哪一个数据源。
     

      dao

    1. package com.itheima.dao;
    2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    3. import com.itheima.po.User;
    4. public interface UserDao extends BaseMapper<User> {
    5. }

      启动类:

    1. @SpringBootApplication
    2. @MapperScan(basePackages = "com.itheima.dao")
    3. public class DynamicApplication {
    4. public static void main(String[] args) {
    5. SpringApplication.run(DynamicApplication.class,args);
    6. }
    7. }

      4.3.6测试
     

      +当使用DS注解指定master的时候:

      ```

      Service

      DS

    "master"

      public class UserServiceImpl implements UserService{

      Autowired

      private UserDao userDao;

      Override

      public User getById

    Integer id

    {

      return userDao.selectById

    id

    ;

      }

      }

      ```

      重启项目,浏览器发送请求:http://localhost:8080/user/1
     

      得到结果:

      ```

      {"id":1,"name":"zhangsan"}

      ```

      +当使用DS注解指定slave_1的时候

      ```

      Service

      DS

    "slave_1"

      public class UserServiceImpl implements UserService{

      Autowired

      private UserDao userDao;

      Override

      public User getById

    Integer id

    {

      return userDao.selectById

    id

    ;

      }

      }

      ```

      重启项目,浏览器发送请求:http://localhost:8080/user/1
     

      得到结果:

    {"id":1,"name":"wangwu"}

      测试成功。
     

      5.总结
     

      使用mybatisplus的动态数据源切换非常方便,只需添加依赖,并在yaml中配置数据源的名称和地址,并在service的实现类中使用注解来指定实现切换即可。下一章节我们来看看如何使用AOP来实现不需要修改代码就能动态切换数据源。
     

      +添加依赖

      +添加yaml配置

      +在业务方法上或者业务类上添加 DS注解

  • 相关阅读:
    基于微信小程序校园二手交易市场(springboot+ mybatis-plus+mysql+原生微信小程序)
    9月8日上课内容 第一章 rsync远程同步
    Zabbix技术分享——使用Zabbix监控系统日志文件大小
    java计算机毕业设计高原特色农产品网站设计源码+mysql数据库+系统+lw文档+部署
    java基于springboot+jsp鲜活农产品销售商城系统
    外汇天眼:FCA 已向交易应用程序运营商发出警告,要求其停止交易游戏化
    如何在不修改原始数组的情况下反转数组?
    营造激发自驱力注重培养学习力的想法一
    Go For Web:踏入Web大门的第一步——Web 的工作方式
    Socket编程实验
  • 原文地址:https://blog.csdn.net/Blue92120/article/details/127977337