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

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

相关推荐
用户9083246027315 小时前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
用户8307196840821 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解1 天前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解1 天前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记2 天前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者2 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840822 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Java水解2 天前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端
初次攀爬者3 天前
RocketMQ在Spring Boot上的基础使用
java·spring boot·rocketmq
花花无缺3 天前
搞懂@Autowired 与@Resuorce
java·spring boot·后端