如何自己实现多数据源

1、方向明确

拿我们最常用的mybatis来说,它获取数据库连接时就是通过获取DataSource对象的getConnection()方法来获取的,我们可以简单看一下mybatis的源码。当我们使用myabtis框架进行查询、修改操作时,它通过调用query、update等方法来操作数据库

query方法又调用了doQuery方法

而这个doQuery是调用子类SimpleExecutor中的方法

最终层层调用JdbcTransaction中的DataSource对象的getConnection方法

因此我们的方向很明确,只需要在它拿到连接的时候返回不同的DataSource对象就可以实现多数据源

2、实际操作

我们通过AOP实现在进入方法前切换到指定数据源,然后改写DataSource方法逻辑即可,可以让DataSource对象中的getConnection方法中进行处理,来获取方法指定的数据源

因此我们需要先定义一个线程上下文,这个上下文中需要记录我们在当前线程中需要使用的数据源

数据源配置

实现多数据源,必须要配置多数据源的连接信息,我们可以在配置文件中进行编写

DataSourceContextHolder

在这个上下文中,定义了三个方法,设置线程使用的数据源名称、获取、清空

方便我们后面在线程中设置和获取数据源名称

java 复制代码
public class DataSourceContextHolder {


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


    public static void setDataSourceName(String dataSourceName)
    {
        DATA_SOURCE_THREAD_LOCAL.set(dataSourceName);
    }

    public static String getDataSourceName()
    {
        return DATA_SOURCE_THREAD_LOCAL.get();
    }


    public static void clearDataSourceName()
    {
        DATA_SOURCE_THREAD_LOCAL.remove();
    }
}

CustomDataSource

这个类需要继承Spring官方的AbstractRoutingDataSource来实现数据源的动态切换,我们需要重写里面的determineCurrentLookupKey方法,这个方法就是决定使用哪个数据源的key,然后调用父类的getConnection方法即可实现切换数据源

我们需要将本次的数据源信息告诉AbstractRoutingDataSource

所以我们需要返回线程上下文中的数据源名,和告诉当前项目中的数据源信息

上面定义了线程上下文,上下文中存的就是本次使用的数据源名

java 复制代码
public class CustomDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        // 返回本次线程中使用的数据源名称
        return DataSourceContextHolder.getDataSourceName();
    }

    public CustomDataSource(DataSource defaultTargetDataSource, Map<Object, Object> dataSourcesMap)
    {
        // 设置默认使用哪个数据源
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        // 可以使用哪些数据源
        super.setTargetDataSources(dataSourcesMap);
        // 加载数据源
        super.afterPropertiesSet();
    }
}

DataSourceConfig

我们需要定义项目中可以使用的数据源,通过读取配置信息返回成DataSource对象,然后mybatis通过调用这个对象的getConnection方法来获取连接

复制代码
DataSourceProperties对象:数据源的基本信息,因为我们自己定义了两个,所以需要两个
java 复制代码
@Configuration
public class DataSourceConfig {

    // ===== DB1 配置属性 =====
    @Bean
    @ConfigurationProperties("spring.datasource.db1")
    public DataSourceProperties db1Properties() {
        return new DataSourceProperties();
    }

    // ===== DB1 数据源 =====
    @Bean
    public DataSource db1DataSource() {
        return db1Properties()
                .initializeDataSourceBuilder()
                .build();
    }

    // ===== DB2 配置属性 =====
    @Bean
    @ConfigurationProperties("spring.datasource.db2")
    public DataSourceProperties db2Properties() {
        return new DataSourceProperties();
    }

    // ===== DB2 数据源 =====
    @Bean
    public DataSource db2DataSource() {
        return db2Properties()
                .initializeDataSourceBuilder()
                .build();
    }


    @Bean(name = "customDataSource")
    @Primary // 重点:优先使用这个数据源
    public CustomDataSource dataSource(DataSource db1DataSource, DataSource db2DataSource)
    {
        Map<Object, Object> customDataSource = new HashMap<>();
        customDataSource.put("db1",db1DataSource);
        customDataSource.put("db2",db2DataSource);
        return new CustomDataSource(db1DataSource,customDataSource);
    }
}

必须给CustomDataSource添加@Primary表示优先使用这个数据源,然后就可以通过里面的CustomDataSource的父类进行切换

但现在就可以测试了

3、结果测试

我们写三个接口进行测试,分别查db1、db2、和不指定库

这里为了测试简单就不写AOP来处理了,后续可以自己加上

成功

当我们什么数据源都没指定,就走了默认的数据源

相关推荐
北龙云海20 分钟前
AI驱动数据库运维变革:北龙云海智能巡检平台实践与展望
运维·数据库·人工智能
带金箍的至尊宝22 分钟前
系统架构设计师笔记 05:第2章硬件与软件基础,冯诺依曼结构怎么考
数据库·笔记·系统架构
蟕初的梦想1 小时前
Claude Haiku 5.5 效能实测与场景应用指南
数据库·人工智能·大模型
专注API从业者1 小时前
告别复杂页面解析,OpenClaw 快速搭建电商商品监控与数据分析脚本
大数据·数据库·python·数据挖掘·数据分析
naturliche1 小时前
sgx支持数据库环境配置,编译,debug
数据库·编译·sgx
SelectDB2 小时前
Apache Doris x Fluss:面向湖流一体的统一查询与分析
大数据·数据库·数据分析
云贝贝贝2 小时前
Oracle 备份恢复实战:RMAN 备份、恢复与误删闪回
数据库
java1234_小锋2 小时前
【技术专题】Mysql8 数据库 - Mysql8 触发器
数据库·mysql·mysql8
要吃这碗饭2 小时前
AI 智能体如何通过 auth.md 注册 Bright Data 账号
数据库·人工智能·php
薛晓刚2 小时前
Oracle ADG 备库归档丢了怎么办?RMAN 增量前滚实战
数据库·oracle