SpringBoot实现多数据源(六)【dynamic-datasource 多数据源组件】

上一篇文章SpringBoot实现多数据源(五)【多数据源事务控制】

六、dynamic-datasource 多数据源组件


官方文档https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611

基于 SpringBoot 的多数据源组件,功能强悍,支持 Seata 分布式事务

  • 支持 数据源分组,适用于多种场景,纯粹多库、读写分离、一主多从混合模式
  • 支持数据源敏感配置信息加密 ENC()
  • 支持每个数据库独立初始化表结构 scheme 和数据库 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. 本框架只做 切换数据源 这件核心的事情,并不限制你的具体操作,切换了数据源可以做任何CRUD
  2. 配置文件所有以"_"下划线分割的数据源 首部 即为组的名称,相同组名称的数据源会放在一个组下
  3. 切换数据源可以是组名,也可以是具体数据源名称。组名则切换时采用负载均衡算法切换,默认使用轮询
  4. 默认的数据源名称为 master,你可以通过 spring.datasource.dynamic.primary 修改
  5. 方法上的注解优先于类上的注解
  6. DS 支持继承抽象类上的 DS,暂不支持继承接口上的 DS

DynamicDataSource 原理

  1. 通过 DynamicDataSourceAutoConfiguration 自动配置类
  2. 配置了 DynamicRoutingDataSource,相当于前面定义的 DynamicDataSource,用来动态提供数据源
  3. 配置了 DynamicDataSourceAnnotationAdvisor 就相当于前面定义的切面类
  4. 设置了 DynamicDataSourceAnnotationInterceptor,当前 advisor 的拦截器,可以理解为前面定义的环绕通知
  5. 当执行方法,会执行 DynamicDataSourceAnnotationInterceptor#invoke 来进行增强
public Object invoke(MethodInvocation invocation) throws Throwable {
    // 获取当前@DS注解的value值
    String dsKey = this.determineDatasourceKey(invocation);
    // 设置当前数据源的标识ThreadLocal中
    DynamicDataSourceContextHolder.push(dsKey);

    Object var3;
    try {
        // 执行目标方法
        var3 = invocation.proceed();
    } finally {
        DynamicDataSourceContextHolder.poll();
    }

    return var3;
}
  1. 在执行数据库操作的时候,就会调用DataSource.getConnection(),此时DataSource指的就是 DynamicRoutingDataSource
  2. 然后执行模板抽象方法 AbstractRoutingDataSource#determineDataSource,被 DynamicRoutingDataSource 重写后调用
// AbstractRoutingDataSource 抽象类方法
protected abstract DataSource determineDataSource();

public Connection getConnection() throws SQLException {
    String xid = TransactionContext.getXID();
    if (StringUtils.isEmpty(xid)) {
        return this.determineDataSource().getConnection();
    } else {
        String ds = DynamicDataSourceContextHolder.peek();
        ds = StringUtils.isEmpty(ds) ? "default" : ds;
        ConnectionProxy connection = ConnectionFactory.getConnection(ds);
        return (Connection)(connection == null ? this.getConnectionProxy(ds, this.determineDataSource().getConnection()) : connection);
    }
}
// DynamicRoutingDataSource 类中的方法
public DataSource determineDataSource() {
    // 拿到切换的数据源标识
    String dsKey = DynamicDataSourceContextHolder.peek();
    // 通过该表示获取对应的数据源
    return this.getDataSource(dsKey);
}

用例测试

  1. 创建一个 dynamic_datasource_framework 的 SpringBoot 模块,并导入依赖
    • pom.xml
<dependencies>
    <!--jdbc-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <!--web-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--aop-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <!--mybatis-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.4</version>
    </dependency>
    <!--druid-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.8</version>
    </dependency>
    <!--mysql驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.28</version>
    </dependency>
    <!--lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.24</version>
    </dependency>
    <!--dynamic-datasource-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
        <version>3.5.0</version>
    </dependency>
</dependencies>
  1. 应用配置文件
    • application.yml
spring:
  autoconfigure:
    # 排除 Druid 自动配置
    exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
  datasource:
    dynamic:
      # 设置默认的数据源或者数据源组,默认值即为master
      primary: master
      # 严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      strict: false
      datasource:
        master:
          # 3.2.0开始支持SPI可省略此配置
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/write?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
          username: root
          password: 123456
          type: com.alibaba.druid.pool.DruidDataSource
        slave:
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://localhost:3306/read?useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
          username: root
          password: 123456
          type: com.alibaba.druid.pool.DruidDataSource
      # 指定使用 druid 数据源
      druid:
        # 连接池初始化大小
        initial-size: 5
        # 最小空闲连接数
        min-idle: 10
        # 最大连接数
        max-active: 20
        # 配置获取连接等待超时的时间
        maxWait: 60000
        # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
        timeBetweenEvictionRunsMillis: 60000
        # 配置一个连接在池中最小生存的时间,单位是毫秒
        minEvictableIdleTimeMillis: 300000
        # 配置一个连接在池中最大生存的时间,单位是毫秒
        maxEvictableIdleTimeMillis: 900000
        # 配置检测连接是否有效
        validationQuery: SELECT 1 FROM DUAL


        #......省略
        #以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2

mybatis:
  mapper-locations: classpath:com/vinjcent/mapper/**/*.xml
  type-aliases-package: com.vinjcent.pojo
  1. 实体类、Mapper层(Dao层)、Service层
  • People
package com.vinjcent.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class People {

    private String name;

}
  • PeopleMapper
package com.vinjcent.mapper;

import com.vinjcent.pojo.People;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface PeopleMapper {

    List<People> list();

    boolean save(People people);

}
  • PeopleServiceImpl(看方法写Service接口)
package com.vinjcent.service.impl;


import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.vinjcent.mapper.PeopleMapper;
import com.vinjcent.pojo.People;
import com.vinjcent.service.PeopleService;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


import java.util.List;

@Service
public class PeopleServiceImpl implements PeopleService {

    private final PeopleMapper peopleMapper;

    @Autowired
    public PeopleServiceImpl(PeopleMapper peopleMapper) {
        this.peopleMapper = peopleMapper;
    }

    /**
     * 说明: 不能和原生 Spring 事务混合,不使用 @DSTransactional 注解无法开启事务,即事务不会生效
     */

    // 从库,如果按照下划线命名方式配置多个,可以指定前缀即可.如slave_1、slave_2、slave3...,只需要设置salve即可,默认使用负载均衡算法
    @DS("slave")
    @Override
    public List<People> list() {
        return peopleMapper.list();
    }

    @DS("master")
    @Override
    public boolean mSave(People people) {
        return peopleMapper.save(people);
    }

    @DS("slave")
    @Override
    public boolean sSave(People people) {
        boolean save = peopleMapper.save(people);
        return save;
    }

    @DSTransactional
    public boolean save (People people) {
        PeopleService peopleService = (PeopleService) AopContext.currentProxy();
        peopleService.sSave(people);
        peopleService.mSave(people);
        // 模拟事务回滚
        int a = 1 / 0;
        return true;
    }


}
  1. 主启动类,扫描mapper接口、暴露代理对象
package com.vinjcent;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@EnableAspectJAutoProxy(exposeProxy = true)
@MapperScan("com.vinjcent.mapper")
@SpringBootApplication
public class DynamicDatasourceFrameworkApplication {

    public static void main(String[] args) {
        SpringApplication.run(DynamicDatasourceFrameworkApplication.class, args);
    }

}
  1. 运行并测试接口
    • PeopleController
package com.vinjcent.controller;

import com.vinjcent.pojo.People;
import com.vinjcent.service.PeopleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("people")
public class PeopleController {


    private final PeopleService peopleService;

    @Autowired
    public PeopleController(PeopleService peopleService) {
        this.peopleService = peopleService;
    }


    @GetMapping("/list")
    public List<People> getAllPeople() {
        return peopleService.list();
    }

    @GetMapping("/insert")
    public String addPeople() {
        peopleService.save(new People("vinjcent"));
        return "添加成功";
    }

}
  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
dynamic-datasource-spring-boot-starter 是一个用于在 Spring Boot 应用中实现多数据源的开源项目。它提供了简单易用的配置方式和强大的功能,让我们能够轻松地管理和切换多个数据源。 使用 dynamic-datasource-spring-boot-starter 实现多数据源的步骤如下: 1. 引入 dynamic-datasource-spring-boot-starter 依赖。在 Maven 或 Gradle 中添加对该项目的依赖,即可将其引入到项目中。 2. 配置多个数据源。在应用的配置文件中,添加多个数据源的配置信息。可以为每个数据源指定不同的 URL、用户名、密码等参数。 3. 配置数据源路由策略。使用 dynamic-datasource-spring-boot-starter 提供的数据源路由策略,将数据源动态地路由到对应的方法或服务上。 4. 使用注解标识数据源。在需要访问不同数据源的方法或服务上,使用相应的注解标识数据源。dynamic-datasource-spring-boot-starter 提供了一系列的注解,如 @DataSource、@Master、@Slave 等,用于标识不同的数据源。 5. 运行应用。运行应用后,dynamic-datasource-spring-boot-starter 会根据配置和注解的设置,将请求路由到正确的数据源上。 通过以上步骤,我们就能够轻松地实现多数据源的管理和切换。dynamic-datasource-spring-boot-starter 的灵活性和高性能使得在实际项目中使用多数据源变得非常简单和高效。同时,该项目还提供了丰富的监控和调试功能,方便我们对多数据源的运行情况进行监控和优化。总之,dynamic-datasource-spring-boot-starter 是一个强大的工具,为我们实现多数据源提供了便捷和可靠的解决方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Naijia_OvO

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值