grpc + springboot + mybatis-plus 动态配置数据源

前言

这是我在这个网站整理的笔记,关注我,接下来还会持续更新。 作者:神的孩子都在歌唱

grpc + springboot + mybatis-plus 动态配置数据源

    • [一. 源码解析](#一. 源码解析)
      • [1.1 项目初始化](#1.1 项目初始化)
      • [1.2 接口请求时候](#1.2 接口请求时候)
    • [二. web应用](#二. web应用)
    • [三. grpc应用程序](#三. grpc应用程序)

一. 源码解析

1.1 项目初始化

项目初始化的时候会调用com.baomidou.dynamic.datasource.DynamicRoutingDataSource对象的addDataSource方法添加数据源,数据源存进dataSourceMap中。

1.2 接口请求时候

进行数据操作时,方法会被com.baomidou.dynamic.datasource.aop.DynamicDataSourceAnnotationInterceptor拦截

intercept 方法中,会解析方法上的 @DS 注解,获取注解中指定的数据源名称。然后,它会调用 DynamicDataSourceContextHolder 类的 setDataSource 方法来切换数据源。

DynamicDataSourceContextHolder 是 MyBatis-Plus 提供的一个线程安全的上下文工具类,用于保存当前线程使用的数据源名称。

进行数据操作时,会调用org.springframework.jdbc.datasource.getConnection()方法;getConnection()方法最终调用了com.baomidou.dynamic.datasource.ds.AbstractRoutingDataSource的getConnection()方法

上面的determineDataSource是由子类com.baomidou.dynamic.datasource.DynamicRoutingDataSource实现,可以看到DynamicRoutingDataSource从DynamicDataSourceContextHolder获取数据源名称

此时的datasource已经切换成了我们需要的数据源

4、数据操作完成后,方法返回第二步中的拦截器,执行DynamicDataSourceContextHolder.poll();清除掉此次Threadlocal中的数据源,避免影响后续数据操作。

注意:不可在事务中切换数据库,保证事务需要方法使用同一连接,使用@DS(dataSourceOne)方法调用@DS(dataSourceTwo)无法切换连接,会导致方法报错。

二. web应用

参考文档

  1. 引入依赖dynamic-datasource-spring-boot-starter
xml 复制代码
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
  <version>${version}</version>
</dependency>
  1. 配置数据源
yml 复制代码
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
    dynamic:
      # 设置默认的数据源或者数据源组,默认值即为 master
      primary: master
      datasource:
        # 主库数据源
        master:
          driverClassName: org.postgresql.Driver
          url: jdbc:postgresql://xx.xx.xx.xx:5432/hwaf?currentSchema=common
          username: root
          password: 123456
        # 主库数据源
        master1:
          driverClassName: org.postgresql.Driver
          url: jdbc:postgresql://xx.xx.xx.xx:5432/hwaf?currentSchema=common
          username: root
          password: 123456
  1. 使用 @DS 切换数据源。

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

注解 结果
没有@DS 默认数据源
@DS("dsName") dsName可以为组名也可以为具体某个库的名称
java 复制代码
@Service
@DS("slave")
public class UserServiceImpl implements UserService {

  @Autowired
  private JdbcTemplate jdbcTemplate;

  public List selectAll() {
    return  jdbcTemplate.queryForList("select * from user");
  }
  
  @Override
  @DS("slave_1")
  public List selectByCondition() {
    return  jdbcTemplate.queryForList("select * from user where age >10");
  }
}
  1. 根据传递的参数动态切换数据源

您可以按照以下步骤使用mybatis-plush的dynamic-datasource-spring-boot-starter来根据前端传递的参数动态切换数据源:

在您的Spring Boot应用程序中添加dynamic-datasource-spring-boot-starter依赖项。

在应用程序配置文件中定义数据源配置信息,包括主数据源和其他数据源。

在需要动态切换数据源的方法上使用@DS注释指定数据源,例如:

java 复制代码
@DS("#dataSourceName")
public List<User> getUserList(String dataSourceName) {
    // 方法实现
}

在这个例子中,#dataSourceName表示从方法参数中获取数据源名称,并将其用作数据源选择策略。

当调用getUserList方法时,将要使用的数据源名称作为参数传递。例如:

java 复制代码
List<User> userList = userService.getUserList("dataSource2");

这将使用名为dataSource2的数据源来执行getUserList方法。

这个在mybatis-plush官方文档中就有了,接下来说的才是我在开发过程中遇到的问题和解决方案

三. grpc应用程序

我想实现的需求是根据传递的参数动态切换数据源,可是我使用的是grpc,没办法像web一样在接口去传递参数

为了改动最小,我打算使用拦截器的方式获取传递的值

java 复制代码
@Slf4j
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Component
public class DataSourceInterceptor implements ServerInterceptor {

    private static final Metadata.Key<String> DATA_SOURCE_KEY =
        Metadata.Key.of("data-source", Metadata.ASCII_STRING_MARSHALLER);

    @Override
    public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
        ServerCall<ReqT, RespT> call,
        Metadata headers,
        ServerCallHandler<ReqT, RespT> next) {
        String dataSource = headers.get(DATA_SOURCE_KEY);
        if (dataSource != null) {

           DynamicDataSourceContextHolder.setDataSource(dataSource);
        }

        return Contexts.interceptCall(Context.current(), call, headers, next);

    }
}

然后起一个上下文线程去存储这个值

java 复制代码
public class DynamicDataSourceContextHolder {

    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    public static void setDataSource(String dataSource) {
        CONTEXT_HOLDER.set(dataSource);
    }

    public static String getDataSource() {
        return CONTEXT_HOLDER.get();
    }

    public static void clearDataSource() {

        CONTEXT_HOLDER.remove();
    }
}

然后重写DsProcessor对象中的doDetermineDatasource,目的是为了能够获取你存储在DynamicDataSourceContextHolder的数据源参数

java 复制代码
@Component
public class DsMetaProcessor extends DsProcessor {
    private static final String DATE_PREFIX = "#dataSource";

    public DsMetaProcessor() {
    }


    @Override
    public boolean matches(String key) {
        return key.startsWith("#dataSource");
    }

    @Override
    public String doDetermineDatasource(MethodInvocation invocation, String key) {

        try {
            return DynamicDataSourceContextHolder.getDataSource();
        } finally {
            // 在执行后清理数据源
            DynamicDataSourceContextHolder.clearDataSource();
        }

    }
}

最后在方法接口上通过以下方式注入就可以了

作者:神的孩子都在歌唱

本人博客:https://blog.csdn.net/weixin_46654114

转载说明:务必注明来源,附带本人博客连接。

相关推荐
皮皮林5513 小时前
SpringBoot 加载外部 Jar,实现功能按需扩展!
java·spring boot
考虑考虑6 小时前
feign异常处理
spring boot·后端·spring
gorgor在码农8 小时前
Spring Boot多数据源切换:三种实现方式详解与实战
java·spring boot·后端·mybatis·mybatis plus·多数据源切换
言一木9 小时前
【springboot组件开发】三方中间件自定义自动装载(rabbitmq/rocketmq/cmq)
spring boot·java-rocketmq·java-rabbitmq·中组件开发
chanalbert9 小时前
Spring Boot诞生背景:从Spring的困境到设计破局
java·spring boot·spring
武昌库里写JAVA9 小时前
【微服务】134:SpringCloud
java·开发语言·spring boot·学习·课程设计
cyc&阿灿10 小时前
深度解析SpringBoot自动化部署实战:从原理到最佳实践
spring boot·后端·自动化
爱瑞瑞10 小时前
云原生学习笔记(七) Docker 实战:使用 Docker 快速构建 Oracle 12c 容器
docker·oracle
麦兜*11 小时前
Spring Boot 集成国内AI,包含文心一言、通义千问和讯飞星火平台实战教程
java·人工智能·spring boot·后端·spring·ai·文心一言
麦兜*12 小时前
【为什么InnoDB用B+树?从存储结构到索引设计深度解析】
java·数据结构·spring boot·b树·mysql·算法·数据库架构