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

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

相关推荐
程序员张32 小时前
Maven编译和打包插件
java·spring boot·maven
灵犀学长4 小时前
EasyExcel之SheetWriteHandler:解锁Excel写入的高阶玩法
spring boot·excel
zwjapple4 小时前
docker-compose一键部署全栈项目。springboot后端,react前端
前端·spring boot·docker
DuelCode7 小时前
Windows VMWare Centos Docker部署Springboot 应用实现文件上传返回文件http链接
java·spring boot·mysql·nginx·docker·centos·mybatis
优创学社27 小时前
基于springboot的社区生鲜团购系统
java·spring boot·后端
幽络源小助理7 小时前
SpringBoot基于Mysql的商业辅助决策系统设计与实现
java·vue.js·spring boot·后端·mysql·spring
猴哥源码7 小时前
基于Java+springboot 的车险理赔信息管理系统
java·spring boot
简佐义的博客9 小时前
破解非模式物种GO/KEGG注释难题
开发语言·数据库·后端·oracle·golang
Code blocks9 小时前
使用Jenkins完成springboot项目快速更新
java·运维·spring boot·后端·jenkins
荔枝吻9 小时前
【沉浸式解决问题】idea开发中mapper类中突然找不到对应实体类
java·intellij-idea·mybatis