springBoot-Mybatis-Plus 多数据源切换实现

这篇具有很好参考价值的文章主要介绍了springBoot-Mybatis-Plus 多数据源切换实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言:本文主要通过AbstractRoutingDataSource,实现根据 http 访问携带的标识动态切换数据源;

1 AbstractRoutingDataSource 介绍:

AbstractRoutingDataSource 是 Spring 框架中的一个抽象类,它可以用来实现动态数据源切换。在多数据源场景下,AbstractRoutingDataSource 可以根据不同的请求来动态地选择合适的数据源进行操作,以达到高效利用多个数据源的目的。

AbstractRoutingDataSource 并不是直接连接数据库的数据源,它只是一个路由数据源,它负责根据一定的规则选择一个真正的数据源来执行数据操作。它的作用可以归纳为以下几点:

(1). 实现多数据源动态切换:AbstractRoutingDataSource 可以通过动态的选定数据源,达到多数据源操作的目的。特别在分布式环境中,可以根据业务需求将数据进行分片,然后将不同的分片数据存储在不同的数据库中,这样就能实现数据负载均衡和高可用性。

(2)… 封装数据库连接池和连接的获取逻辑:AbstractRoutingDataSource 通过封装多个数据源连接池的实现细节,屏蔽底层数据源的细节,使得业务代码不需要关心连接的获取和释放,从而简化了业务代码的编写。

(3). 实现数据源的动态切换:AbstractRoutingDataSource 可以通过动态切换数据源,实现数据源的动态切换,从而在不影响系统正常运行的情况下,能够对数据源进行升级、迁移等操作。

综上所述,AbstractRoutingDataSource 的主要作用是实现多数据源的动态切换,这在多个数据库之间实现负载均衡、数据迁移、分片存储等场景下,可以大大提高系统的性能和可用性。

2 springBoot 集成:

2.1 pom.xml 引入jar:

 <dependency>
    <groupId>com.baomidou</groupId>
     <artifactId>mybatis-plus-boot-starter</artifactId>
     <version>3.5.2</version>
 </dependency>
 <dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>8.0.21</version>
 </dependency>

2.2 数据源解析类:
DataSourceConfig:


import com.example.dynamicdemo.config.DynamicDataSource;
import com.zaxxer.hikari.HikariDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

import javax.sql.DataSource;
import java.util.*;

/**
 * @author du_imba
 */
@Configuration
public class DataSourceConfig {

    private static final Logger logger = LoggerFactory.getLogger(DataSourceConfig.class);

    @Autowired
    private Environment environment;
    private static final String SEP = ",";

    @Bean
    public DataSource getDynamicDataSource() {
        DynamicDataSource routingDataSource = new DynamicDataSource();
        List<String> dataSourceKeys = new ArrayList<>();

        Iterable sources = ConfigurationPropertySources.get(environment);
        Binder binder = new Binder(sources);



        BindResult bindResult = binder.bind("datasource.tinyid", Properties.class);
        Properties properties= (Properties) bindResult.get();
        String names = properties.getProperty("names");
        String dataSourceType = properties.getProperty("type");


//        RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(environment, "datasource.tinyid.");
//        String names = propertyResolver.getProperty("names");
//        String dataSourceType = propertyResolver.getProperty("type");

        Map<Object, Object> targetDataSources = new HashMap<>(4);
        routingDataSource.setTargetDataSources(targetDataSources);
        routingDataSource.setDataSourceKeys(dataSourceKeys);
        // 多个数据源
        for (String name : names.split(SEP)) {

            Map<String, Object> dsMap = getSubProperties(name + ".",properties);
            DataSource dataSource = buildDataSource(dataSourceType, dsMap);
            buildDataSourceProperties(dataSource, dsMap);
            targetDataSources.put(name, dataSource);
            dataSourceKeys.add(name);
        }
        // 设置默认数据源
        routingDataSource.setDefaultTargetDataSource(targetDataSources.get("primary"));
        return routingDataSource;
    }

    private Map<String, Object> getSubProperties(String s,Properties properties) {
        Map<String, Object> dsMap = new HashMap<>(1<<2);

        dsMap.put("driver-class-name",properties.get(s+"driver-class-name"));
        dsMap.put("url",properties.get(s+"url"));
        dsMap.put("username",properties.get(s+"username"));
        dsMap.put("password",properties.get(s+"password"));
        return dsMap;
    }

    private void buildDataSourceProperties(DataSource dataSource, Map<String, Object> dsMap) {
        try {
            // 此方法性能差,慎用
            BeanUtils.copyProperties(dataSource, dsMap);
        } catch (Exception e) {
            logger.error("error copy properties", e);
        }
    }

    private HikariDataSource buildDataSource(String dataSourceType, Map<String, Object> dsMap) {
        try {
//            String className = DEFAULT_DATASOURCE_TYPE;
//            if (dataSourceType != null && !"".equals(dataSourceType.trim())) {
//                className = dataSourceType;
//            }
//            Class<? extends DataSource> type = (Class<? extends DataSource>) Class.forName(className);
            String driverClassName = dsMap.get("driver-class-name").toString();
            String url = dsMap.get("url").toString();
            String username = dsMap.get("username").toString();
            String password = dsMap.get("password").toString();

            return DataSourceBuilder.create()
                    .driverClassName(driverClassName)
                    .url(url)
                    .username(username)
                    .password(password)
//                    .type(type)
                    .type(HikariDataSource.class)
                    .build();

        } catch (Exception e) {
            logger.error("buildDataSource error", e);
            throw new IllegalStateException(e);
        }
    }


}

DynamicDataSource 路由db:


import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import java.util.List;
import java.util.Random;

/**
 * @author du_imba
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    private  static  final ThreadLocal<String> threadLocal = new ThreadLocal<>();

    private List<String> dataSourceKeys;

    @Override
    protected Object determineCurrentLookupKey() {
//        if(dataSourceKeys.size() == 1) {
//            return dataSourceKeys.get(0);
//        }
//        Random r = new Random();
//        return dataSourceKeys.get(r.nextInt(dataSourceKeys.size()));
        return getDb();
    }

    public List<String> getDataSourceKeys() {
        return dataSourceKeys;
    }

    public void setDataSourceKeys(List<String> dataSourceKeys) {
        this.dataSourceKeys = dataSourceKeys;
    }

    public static void setDb(String db){
        threadLocal.set(db);
    }
    public static String getDb(){
        return threadLocal.get();
    }
    public static void clear() {
        threadLocal.remove();
    }
}

2.3 拦截http 请求,设置本次访问的db:
HttpRequestDynamic:




import lombok.extern.slf4j.Slf4j;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
public class HttpRequestDynamic implements HandlerInterceptor {
    final static ThreadLocal<Boolean> threadLocal = new ThreadLocal<>();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod)) {
            // 不是 httpreuqest 请求直接放行
            return true;
        }
        DynamicDataSource.setDb(request.getHeader("db"));
        threadLocal.set(true);

        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 在方法执行完毕或者执行报错后,移除数据源
        if (null != threadLocal.get() && threadLocal.get()) {
            DynamicDataSource.clear();
        }
        threadLocal.remove();
    }
}

WebConfiguration 拦截:


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@Import({HttpRequestDynamic.class})
public class WebConfiguration implements WebMvcConfigurer {
    @Autowired
    private HttpRequestDynamic httpRequestDynamic;

    /**
     * 拦截器配置
     *
     * @param registry 注册类
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(httpRequestDynamic).addPathPatterns("/**")
                .excludePathPatterns(
                        "/file/get/*",
                        "/image/view/*",
                        "/**/error"
                );
    }
}

2.4 application.properties

server.port=9999
server.context-path=/tinyid

batch.size.max=100000

#datasource.tinyid.names=primary
datasource.tinyid.names=primary,secondary

datasource.tinyid.primary.driver-class-name=com.mysql.cj.jdbc.Driver
datasource.tinyid.primary.url=jdbc:mysql://localhost:3406/d1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
datasource.tinyid.primary.username=root
datasource.tinyid.primary.password=ddsoft
#datasource.tinyid.primary.maxActive=10

datasource.tinyid.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
datasource.tinyid.secondary.url=jdbc:mysql://localhost:3406/d2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
datasource.tinyid.secondary.username=root
datasource.tinyid.secondary.password=ddsoft
#datasource.tinyid.secondary.testOnBorrow=false
#datasource.tinyid.secondary.maxActive=10



2.5 请求:header 头放入本次的db
mybatisplus多数据源切换,Spring框架篇,db数据库,# spring-boot,mybatis,spring boot,java

3 总结:

本文主要通过拦截http 请求,解析本次要访问的db,然后将db 设置到DynamicDataSource的ThreadLocal 中,使得访问数据库时获取到对应的db连接完成操作;

git 地址参考:https://codeup.aliyun.com/61cd21816112fe9819da8d9c/dynamic-demo.git文章来源地址https://www.toymoban.com/news/detail-772418.html

到了这里,关于springBoot-Mybatis-Plus 多数据源切换实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【SpringBoot 3.x】整合Mybatis-Plus多数据源、Druid

    开发依赖 版本 Spring Boot 3.0.6 Mybatis-Plus 3.5.3.1 dynamic-datasource-spring-boot-starter 3.6.1 JDK 20 SpringBoot启动类修改 由于排除了DruidDataSourceAutoConfigure类的自动装载,就需要手工指定装配以下几个类 查看DruidDataSourceAutoConfigure这个类的源码可以看出,需要把@Import带进来的几个类进行自动装

    2024年02月04日
    浏览(51)
  • Dynamic DataSource 多数据源配置【 Springboot + DataSource + MyBatis Plus + Druid】

    MybatisPlus多数据源配置主要解决的是多数据库连接和切换的问题。在一些大型应用中,由于数据量的增长或者业务模块的增多,可能需要访问多个数据库。这时,就需要配置多个数据源。 2.1.1、引用依赖 2.1.2、application.yml 配置 2.1.3、通用配置类 2.1.4、使用方式 这里便不过多的

    2024年02月03日
    浏览(48)
  • SpringBoot+MyBatis-Plus多数据源@DS注解失效的解决方法

    引入 dynamic-datasource: application.yml 数据源配置: 详细使用请看 MyBatis-Plus官网 这种场景还是比较常见,比如在一个为master数据源的调用slave数据源就会失效 slave数据源Service方法 mater数据源Service方法调用slave数据源Service方法 这里会出现没有走slave_1,依然还是master数据源 需要在

    2024年01月18日
    浏览(52)
  • java_Springboot_Mybatis-Plus_自定义多数据源MybatisSqlSessionFactoryBean配置

    需要在服务中集成表结构维护的功能,维护表结构就需要使用具有执行DDL脚本权限的账号。 为了保证系统的安全性,考虑在工程中配置多个数据源引入不同权限账号,高权限账号只在特定逻辑中使用,其它默认业务使用低权限账号。 加入新的数据源不能影响已有的功能,保

    2024年01月16日
    浏览(45)
  • Springboot+mybatis-plus+dynamic-datasource+Druid 多数据源 分布式事务

    背景 处理多数据源事务一直是一个复杂而棘手的问题,通常我们有两种主流的解决方法。 第一种是通过Atomikos手动创建多数据源事务,这种方法更适合数据源数量较少,参数配置不复杂,对性能要求不高的项目。然而,这种方法的最大困难在于需要手动配置大量设置,这可能

    2024年02月11日
    浏览(40)
  • mybatis(plus)多数据源

         一个项目大部分都是单一数据库多一些,但是有时候会需要用多个库,所以这时候据需要使用多数据源。我这里使用springboot+mybatis(plus)+druid多数据源. 目前我知道有两种方式,一种方式是需要在service实现类上添加@DS,一种方式是通过配置的方式,配置不同的SqlSessionFac

    2024年02月06日
    浏览(44)
  • 利用 Mybatis-Plus 的动态数据源实现多数据源配置

    目录 一、导入依赖 二、Application.yaml配置文件 三、切换数据源 四、其他方法 4.1 配置多个数据源 4.2 定义Datasource和EntityManager 4.3 在需要使用数据源的地方注入不同的EntityManager 官网:https://baomidou.com/pages/a61e1b/#dynamic-datasource 默认是使用配置文件中master参数设置的数据库。

    2024年02月13日
    浏览(43)
  • Mybatis-plus动态数据源

    由于服务没有做微服务部署,需要在后台管理系统访问其他服务的库,所以需要用到动态数据源切换 引入依赖 mybatis-plus动态数据源依赖 更改配置 配置类 添加注解 @DS注解我一般放在dao层,因为觉得这样更合理 启动测试 问题: 动态数据源切换时效 当服务层接口添加事务注解

    2024年04月12日
    浏览(52)
  • Spring Boot MyBatis Plus 配置数据源详解

    🎉欢迎来到架构设计专栏~Spring Boot MyBatis Plus 配置数据源详解 ☆* o(≧▽≦)o *☆嗨~我是IT·陈寒🍹 ✨博客主页:IT·陈寒的博客 🎈该系列文章专栏:架构设计 📜其他专栏:Java学习路线 Java面试技巧 Java实战项目 AIGC人工智能 数据结构学习 🍹文章作者技术和水平有限,如果文

    2024年01月21日
    浏览(56)
  • Mybatis-plus多数据源单元测试@MybatisPlusTest

    mybatis-plus多数据源单元测试报错 错误原因分析 多数据源,但是不能取到数据信息 解决方案 在注解中添加 @ImportAutoConfiguration(value = {RmasDataSourceConfig.class}, exclude = DataSourceAutoConfiguration.class) 注意事项 1.@Test添加以后,没有启动键,后来发现引入的包不对,必须引入 org.junit.jup

    2024年02月11日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包