dynamic-datasource 多数据源集成springboot(一)

7,125 阅读9分钟

引言

       mybatis-plus 的多数据源组件  dynamic-datasource ,看介绍和网上的评价还不错,于是就想试试看。之前用多数据源用过若依项目里面的多数据源,比较麻烦。因此测试下集成非常简单好用。

介绍

dynamic-datasource-spring-boot-starter  是一个基于springboot的快速集成多数据源的启动器,看了一下确实很不错。值得推荐。

特性

  • 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。
  • 支持数据库敏感配置信息 加密 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的分布式事务方案。
  • 提供 本地多数据源事务方案。 附:不能和原生spring事务混用。

这是文档上的介绍,我会进行分批次演示,本文介绍最基础的用法。

使用

1、引入依赖(当前最新版本3.5.1)

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

<!-- mybatis-plus -->
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-boot-starter</artifactId>
  <version>3.5.2</version>
</dependency>

2、数据库表

所有数据库只有 yixi_user 表,从数据上可以看出来是具体哪个库的数据,只是作为演示用,实际业务中主库和从库的数据是一样的。

CREATE TABLE `yx_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) DEFAULT NULL COMMENT '姓名',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

实体类代码

package com.azydbly.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * <p>
 * 
 * </p>
 *
 * @author yixi
 * @since 2022-08-19
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("yx_user")
public class YxUser implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 姓名
     */
    private String name;

}

3.1、一主多从配置

一主多从,master 配置的为主库(默认),slave 为从库组

server:
  port: 8081

spring:
  datasource:
    dynamic:
      # 设置默认的数据源或者数据源组,默认值即为master
      primary: master
      # 严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      strict: false
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/dynamic_master1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
          username: root
          password: 123456
          # 3.2.0开始支持SPI可省略此配置,其他库配置我进行了省略依旧可以使用
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave_1:
          url: jdbc:mysql://localhost:3306/dynamic_slave1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
          username: root
          password: 123456
        slave_2:
          url: jdbc:mysql://localhost:3306/dynamic_slave2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
          username: root
          password: 123456
        slave_3:
          url: jdbc:mysql://localhost:3306/dynamic_slave3?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
          username: root
          password: 123456
      # 自定义负载均衡策略。slave组下有;多个个数据源,当用户使用 slave 切换数据源时会使用负载均衡算法。系统自带了两个负载均衡算法 LoadBalanceDynamicDataSourceStrategy 轮询,是默认的。 RandomDynamicDataSourceStrategy 随机的。
      strategy: com.baomidou.dynamic.datasource.strategy.LoadBalanceDynamicDataSourceStrategy

默认使用 master 数据库,如何使用其他数据库,在类或者方法上增加注解 @DS("slave_1") 这样就可以使用 slave_1 数据库了,其他代码不用进行修改。

controller 代码

package com.azydbly.controller;


import com.azydbly.service.IYxUserService;
import com.baomidou.dynamic.datasource.annotation.DS;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;

/**
 * <p>
 *  前端控制器
 *  一主多从
 *  @DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解。
 * </p>
 *
 * @author yixi
 * @since 2022-08-19
 */
@RestController
@RequestMapping("/user")
public class YxUserController {

    @Resource
    private IYxUserService yxUserService;


    /**
     * 获取所有用户(主库)
     *
     * @return
     */
    @GetMapping
    public List getAllUser() {
        return yxUserService.list();
    }

    /**
     * 获取所有用户(从库1)
     *
     * @return
     */
    @GetMapping("slave1")
    @DS("slave_1")
    public List getAllUserForSlaveOne() {
        return yxUserService.list();
    }

    /**
     * 获取所有用户(从库2)
     *
     * @return
     */
    @GetMapping("slave2")
    @DS("slave_2")
    public List getAllUserForSlaveTwo() {
        return yxUserService.list();
    }

    /**
     * 获取所有用户(从库3)
     *
     * @return
     */
    @GetMapping("slave3")
    @DS("slave_3")
    public List getAllUserForSlaveThree() {
        return yxUserService.list();
    }

    /**
     * 获取从库数据源
     * 默认是从 slave 数据组中的数据库进行轮训访问,可修改配置文件进行随机访问。 默认为轮训访问
     *
     * @return
     */
    @GetMapping("slave")
    @DS("slave")
    public List getAllUserForSlave() {
        return yxUserService.list();
    }

}

3.2、多主多从配置

多主多从,master 配置的为主库组,从库组

server:
  port: 8081


spring:
  datasource:
    dynamic:
      # 设置默认的数据源或者数据源组,默认值即为master
      primary: master
      # 严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      strict: false
      datasource:
        master_1:
          url: jdbc:mysql://localhost:3306/dynamic_master1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
          username: root
          password: 123456
          # 3.2.0开始支持SPI可省略此配置,其他库配置我进行了省略依旧可以使用
          driver-class-name: com.mysql.cj.jdbc.Driver
        master_2:
          url: jdbc:mysql://localhost:3306/dynamic_master2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
          username: root
          password: 123456
        slave_1:
          url: jdbc:mysql://localhost:3306/dynamic_slave1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
          username: root
          password: 123456
        slave_2:
          url: jdbc:mysql://localhost:3306/dynamic_slave2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
          username: root
          password: 123456
        slave_3:
          url: jdbc:mysql://localhost:3306/dynamic_slave3?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
          username: root
          password: 123456
      # 自定义负载均衡策略。slave组下有;多个个数据源,当用户使用 slave 切换数据源时会使用负载均衡算法。系统自带了两个负载均衡算法 LoadBalanceDynamicDataSourceStrategy 轮询,是默认的。 RandomDynamicDataSourceStrategy 随机的。
      strategy: com.baomidou.dynamic.datasource.strategy.LoadBalanceDynamicDataSourceStrategy

controller 代码

package com.azydbly.controller;


import com.azydbly.service.IYxUserService;
import com.baomidou.dynamic.datasource.annotation.DS;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;

/**
 * <p>
 *  前端控制器
 *  多主多从,主要演示多主,多从和一主多从是一样的
 *  @DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解。
 * </p>
 *
 * @author yixi
 * @since 2022-08-19
 */
@RestController
@RequestMapping("/more/user")
public class MoreUserController {

    @Resource
    private IYxUserService yxUserService;


    /**
     * 获取所有用户(主库)
     *
     * @return
     */
    @GetMapping
    public List getAllUser() {
        return yxUserService.list();
    }

    /**
     * 获取从库数据源
     * 默认是从 slave 数据组中的数据库进行轮训访问,可修改配置文件进行随机访问。 默认为轮训访问
     *
     * @return
     */
    @GetMapping("master")
    @DS("master")
    public List getAllUserForSlave() {
        return yxUserService.list();
    }
}

官网还给出了其他的模式组合,这里不在进行赘述,有兴趣的小伙伴可以进行研究

# 多主多从                      纯粹多库(记得设置primary)                   混合配置
spring:                               spring:                               spring:
  datasource:                           datasource:                           datasource:
    dynamic:                              dynamic:                              dynamic:
      datasource:                           datasource:                           datasource:
        master_1:                             mysql:                                master:
        master_2:                             oracle:                               slave_1:
        slave_1:                              sqlserver:                            slave_2:
        slave_2:                              postgresql:                           oracle_1:
        slave_3:                              h2:                                   oracle_2:

4、集成数据源

可以集成 Druid、HikariCP、BeeCP、DBCP2、Jndi,这里拿一主多从进行讲解集成 druid 数据源。

引入 druid 依赖

<!-- 阿里数据库连接池 druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.9</version>
</dependency>            

配置文件(druid的参数之前的druid的用法一样)

server:
  port: 8081

spring:
  datasource:
    druid:
      statViewServlet:
        enabled: true
        # 设置白名单,不填则允许所有访问
        allow:
        # url-pattern: /druid/*
        # 控制台管理用户名和密码
        login-username: admin
        login-password: 123456
    dynamic:
      # 以下是支持的全局默认值
      druid:
        # 初始连接数
        initialSize: 5
        # 最小连接池数量
        minIdle: 10
        # 最大连接池数量
        maxActive: 20
        # 配置获取连接等待超时的时间
        maxWait: 60000
        # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
        timeBetweenEvictionRunsMillis: 60000
        # 配置一个连接在池中最小生存的时间,单位是毫秒
        minEvictableIdleTimeMillis: 300000
        # 配置一个连接在池中最大生存的时间,单位是毫秒
        maxEvictableIdleTimeMillis: 900000
        # 配置检测连接是否有效
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        webStatFilter:
          enabled: true
        filter:
          stat:
            enabled: true
            # 慢SQL记录
            log-slow-sql: true
            slow-sql-millis: 1000
            merge-sql: true
          wall:
            config:
              multi-statement-allow: true
      # 设置默认的数据源或者数据源组,默认值即为master
      primary: master
      # 严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      strict: false
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/dynamic_master1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
          username: root
          password: 123456
          # 3.2.0开始支持SPI可省略此配置,其他库配置我进行了省略依旧可以使用
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave_1:
          url: jdbc:mysql://localhost:3306/dynamic_slave1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
          username: root
          password: 123456
        slave_2:
          url: jdbc:mysql://localhost:3306/dynamic_slave2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
          username: root
          password: 123456
        slave_3:
          url: jdbc:mysql://localhost:3306/dynamic_slave3?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
          username: root
          password: 123456
      # 自定义负载均衡策略。slave组下有;多个个数据源,当用户使用 slave 切换数据源时会使用负载均衡算法。系统自带了两个负载均衡算法 LoadBalanceDynamicDataSourceStrategy 轮询,是默认的。 RandomDynamicDataSourceStrategy 随机的。
      strategy: com.baomidou.dynamic.datasource.strategy.LoadBalanceDynamicDataSourceStrategy

5、自定义注解

在数据源确定的情况下,可以进行自定义注解

package com.azydbly.datasource;

import com.baomidou.dynamic.datasource.annotation.DS;

import java.lang.annotation.*;

/**
 * @ClassName: SlaveOne
 * @Description: 数据源-从库1
 * @Author: yi•xi
 * @CreateDate: 2022/8/19 21:35
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@DS("slave_1")
public @interface SlaveOne {
}

用法,和 @DB() 的用法一样, 在需要的方法或者类上加 @SlaveOne 即可

controller 代码

package com.azydbly.controller;

import com.azydbly.datasource.SlaveOne;
import com.azydbly.datasource.SlaveThree;
import com.azydbly.datasource.SlaveTwo;
import com.azydbly.service.IYxUserService;
import com.baomidou.dynamic.datasource.annotation.DS;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;

/**
 * <p>
 *  前端控制器
 *  自定义注解
 * </p>
 *
 * @author yixi
 * @since 2022-08-19
 */
@RestController
@RequestMapping("/custom/user")
public class CustomUserController {

    @Resource
    private IYxUserService yxUserService;


    /**
     * 获取所有用户(从库1)
     *
     * @return
     */
    @GetMapping("slave1")
    @SlaveOne
    public List getAllUserForSlaveOne() {
        return yxUserService.list();
    }

    /**
     * 获取所有用户(从库2)
     *
     * @return
     */
    @GetMapping("slave2")
    @SlaveTwo
    public List getAllUserForSlaveTwo() {
        return yxUserService.list();
    }

    /**
     * 获取所有用户(从库3)
     *
     * @return
     */
    @GetMapping("slave3")
    @SlaveThree
    public List getAllUserForSlaveThree() {
        return yxUserService.list();
    }
}

总结

其实官网上还有很多用法,集成也非常简单。感兴趣的小伙伴可以购买官网的文档进行学习。在集成中遇到问题也可以来进行探讨。

接口文档地址:www.apifox.cn/apidoc/shar…

项目地址:gitee.com/azydbly_adm…

懿曦:早晨的太阳