当前位置: 首页 > article >正文

SpringBoot动态配置数据源的几种实现方式

一、Apache Druid方式

1、配置文件

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    master:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&generateSimpleParameterMetadata=true&autoReconnect=true&useSSL=true
      username: root
      password: root3306
    slave:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&generateSimpleParameterMetadata=true&autoReconnect=true&useSSL=true
      username: root
      password: root3306
    druid-pool:
      #连接池的最大数据库连接数。设为0表示无限制
      max-active: 20
      #初始化数量
      initial-size: 2
      #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制
      max-wait: 60000
      #最小空闲连接:连接池中容许保持空闲状态的最小连接数量,低于这个数量将创建新的连接
      min-idle: 2

2、配置数据源

配置数据源基本信息,并将所有数据源添加到动态数据源中进行进行管理

@Configuration
public class DynamicDataSourceConfig {

    @Value("${spring.datasource.type}")
    private Class<? extends DataSource> dataSourceType;

    @Value("${spring.datasource.druid-pool.initial-size}")
    private int initialSize;
    @Value("${spring.datasource.druid-pool.max-active}")
    private int maxActive;
    @Value("${spring.datasource.druid-pool.max-wait}")
    private int maxWait;
    @Value("${spring.datasource.druid-pool.min-idle}")
    private int minIdle;

    @Primary
    @Bean("masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        DruidDataSource druidDataSource = (DruidDataSource) DataSourceBuilder.create().type(dataSourceType).build();
        druidPoolConfig(druidDataSource);
        return druidDataSource;
    }

    @Bean("slaveDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource() {
        DruidDataSource druidDataSource = (DruidDataSource) DataSourceBuilder.create().type(dataSourceType).build();
        druidPoolConfig(druidDataSource);
        return druidDataSource;
    }

    private void druidPoolConfig(DruidDataSource druidDataSource) {
        druidDataSource.setMaxActive(maxActive);
        druidDataSource.setMaxWait(maxWait);
        druidDataSource.setInitialSize(initialSize);
        druidDataSource.setMinIdle(minIdle);
    }

    @Bean("dataSource")
    @DependsOn({"masterDataSource", "slaveDataSource"})
    public DataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("slaveDataSource") DataSource slaveDataSource) {
        Map<Object, Object> dataSourceMap = new HashMap<>();
        dataSourceMap.put("masterDataSource", masterDataSource);
        dataSourceMap.put("slaveDataSource", slaveDataSource);
        DynamicDataSourceContextHolder.dataSourceNames.add("masterDataSource");
        DynamicDataSourceContextHolder.dataSourceNames.add("slaveDataSource");
        // 设置动态数据源
        return new DynamicDataSource(masterDataSource, dataSourceMap);
    }
}
public class DynamicDataSource extends AbstractRoutingDataSource {

    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
    }

    @Override
    public Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSource();
    }
}

自定义DynamicDataSouce并继承AbstractRoutingDataSource 。AbstractRoutingDataSource 是 Spring 框架中用于实现动态数据源路由的一个抽象类。它通过实现数据源的动态切换来支持多数据源的应用场景。AbstractRoutingDataSource 的核心原理是通过一个线程本地变量(ThreadLocal)来保存当前使用的数据源标识,然后在每次获取连接时,根据这个标识来决定使用哪个数据源。
主要方法包括:

1)determineCurrentLookupKey():这是一个抽象方法,需要子类实现。它用于确定当前使用的数据源标识。根据返回的标识,AbstractRoutingDataSource 会从预先配置的数据源中选择对应的数据源。
2)setTargetDataSources():用于设置数据源的映射关系。这个方法接受一个 Map<Object, Object> 类型的参数,键为数据源标识,值为具体的数据源对象。
3)setDefaultTargetDataSource():用于设置默认的数据源。如果没有找到匹配的数据源标识,就会使用默认的数据源。
4)afterPropertiesSet():检查配置,确保 targetDataSources 和 defaultTargetDataSource 属性已被设置;初始化映射,将指定的默认数据源和目标数据源映射解析并存储到相应的内部变量中。

public class DynamicDataSourceContextHolder {
    private static final ThreadLocal<String> DATASOURCE_CONTEXT_HOLDER = new ThreadLocal<>();
    /**
     * 管理全部数据源
     */
    public static List<String> dataSourceNames = new ArrayList<>();

    /**
     * 判断是否存在指定数据源
     */
    public static boolean containsDataSource(String dataSourceName) {
        return dataSourceNames.contains(dataSourceName);
    }
    /**
     * 设置当前数据源
     */
    public static void setDataSource(String dataSourceName) {
        DATASOURCE_CONTEXT_HOLDER.set(dataSourceName);
    }
    /**
     * 获取当前数据源
     */
    public static String getDataSource() {
        return DATASOURCE_CONTEXT_HOLDER.get();
    }
    /**
     * 清除数据源
     */
    public static void clear() {
        DATASOURCE_CONTEXT_HOLDER.remove();
    }
}

ThreadLocal是Java中的一个工具类,能够为每个线程提供一个独立的变量副本,从而实现线程隔离。利用ThreadLocal管理当前使用的数据源,DynamicDataSourceContextHolder可以在不同的线程间隔离数据源的使用,实现数据源的动态切换。
如果整合使用了Mybatis-Plus,则还需要添加Mybatis-Plus的相关配置,否则会报错,如下所示

@Configuration
@MapperScan(basePackages = {"com.practice.mapper"}, sqlSessionTemplateRef = "sqlTemplateDefault")
public class MybatisConfig {
    @Bean(name = "sqlFactoryDefault")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource)
            throws Exception {
        MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
        factoryBean.setDataSource(dynamicDataSource);
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        factoryBean.setMapperLocations(resolver.getResources("classpath*:mapper/*/*.xml"));
        MybatisConfiguration mybatisConfiguration = new MybatisConfiguration();
        mybatisConfiguration.setLogImpl(StdOutImpl.class);
        factoryBean.setConfiguration(mybatisConfiguration);
        return factoryBean.getObject();
    }

    @Bean(name = "sqlTemplateDefault")
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlFactoryDefault") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

3、利用AOP动态切换

自定义数据源标识注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DS {
    String value() default "masterDataSource";
}

利用AOP实现动态切换数据源

@Component
@Aspect
@Slf4j
public class DynamicDataSourceAspect {
    private static final String DEFAULT_DATASOURCE = "masterDataSource";

    @Pointcut("@annotation(com.practice.annotation.DS)")
    public void dsPointCut() {}

    @Before("dsPointCut() && @annotation(ds)")
    public void changeDataSource(JoinPoint joinPoint, DS ds) {
        String dataSourceName = ds.value();
        if (DynamicDataSourceContextHolder.containsDataSource(dataSourceName)) {
            DynamicDataSourceContextHolder.setDataSource(dataSourceName);
            log.info("切换到数据源:{}", dataSourceName);
        } else {
            log.error("数据源不存在:{}", dataSourceName);
        }
    }

    @After("@annotation(ds)")
    public void clearDataSource(JoinPoint joinPoint, DS ds) {
        DynamicDataSourceContextHolder.clear();
    }
}


http://www.kler.cn/a/612076.html

相关文章:

  • Spring事务与数据库事务的关系
  • 常见邮件协议
  • Oracle Database In-Memory 23ai 新特性
  • 【C++接入大模型】WinHTTP类封装:实现对话式大模型接口访问
  • 适合DBA的brew上手指南
  • (C语言)网络编程之TCP(含三次握手和四次挥手详解)
  • 适配器模式及其典型应用
  • Vue-create-vue-开发流程-项目结构-API风格-ElementPlus-入门准备工作
  • 【保姆级别教程】VMware虚拟机安装Mac OS15苹果系统附带【VMware TooLS安装】【解锁虚拟机】和【Mac OS与真机共享文件夹】手把手教程
  • 分布式共识算法解密:从Paxos到Raft的演进之路
  • 使用string和string_view(一)——C风格字符串、字符串字面量和std::string
  • 批量将 PDF 转换为 Word/PPT/Txt/Jpg图片等其它格式
  • 开发DOM更新算法
  • [python]基于yolov8实现热力图可视化支持图像视频和摄像头检测
  • CentOS 7安装 mysql
  • 老是忘记package.json,备忘一下 webpack 环境下 Vue Cli 和 Vite 命令行工具对比
  • 【Pandas】pandas Series to_xarray
  • SpringBoot集成腾讯云OCR实现身份证识别
  • 【牛客网】数据分析笔试刷题
  • Charles抓HTTPS包