如何自己实现多数据源

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来处理了,后续可以自己加上

成功

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

相关推荐
AI周红伟2 小时前
周红伟:关于OpenClaw安全使用提醒
大数据·数据库·人工智能·安全·腾讯云·openclaw
斯密码赛我是美女2 小时前
周报--2
android·数据库
marsh02062 小时前
23 openclaw防止SQL注入:参数化查询与ORM安全使用
数据库·sql·安全·ai·编程·技术
原来是猿2 小时前
为什么要配置环境变量?
linux·数据库·python
星辰_mya2 小时前
MVCC 与事务隔离:MySQL 如何实现“读不阻塞写”?
java·数据库·mysql·面试·架构
m0_738120722 小时前
渗透测试——Ripper靶机详细横向渗透过程(rips扫描文件,水平横向越权,Webmin直接获取root权限)
linux·网络·数据库·安全·web安全·php
大能嘚吧嘚2 小时前
Redis客户端框架-Redisson
数据库·redis·缓存
神龙斗士2402 小时前
MySQL在Navicat中 库的操作 表的操作
数据库·mysql
攒了一袋星辰2 小时前
10万级用户数据日更与定向推送系统的可靠性设计
java·数据库·算法