AbstractRoutingDataSource实现动态多数据源

动态数据源

​ 动态数据源就是程序在运行过程中,可以在多个数据源之间切换,从而对不同的数据源进行操作。Spring Framework的Jdbc包中,提供了AbstractRoutingDataSource用于实现动态数据源。

实现

​ 可以先自己设计一种动态数据源的实现:在一个数据源池中维护各个不同的数据源,当执行数据库操作时,从数据池中获取对应的数据源链接来进行数据库操作。这样看来我们只需要一个数据源池,而AbstractRoutingDataSource也正是基于这样的原理。

AbstractRoutingDataSource

AbstractRoutingDataSource UML图

示例

​ 下面先来看一个示例,创建DynamicDataSource继承AbstractRoutingDataSource,并覆盖determineCurrentLookupKey()方法。

package com.jdbc;

import javax.sql.DataSource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {

    private static String dataSource;

    @Override
    protected Object determineCurrentLookupKey() {
        return getDataSource();
    }

    public DataSource getCurrentDataSource(){
        return super.determineTargetDataSource();
    }

    public static String getDataSource() {
        return dataSource;
    }

    public static void setDataSource(String dataSource) {
        DynamicDataSource.dataSource = dataSource;
    }

}

​ 创建测试类DynamicDataSourceTest,其中维护了多个数据源,并实现了运行时在不同的数据源中进行操作。

package jdbc;

import com.jdbc.DynamicDataSource;
import com.sun.istack.internal.NotNull;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

public class DynamicDataSourceTest {

    public static final String PRIMARY_DATASOURCE = "PRIMARY";
    public static final String SECOND_DATASOURCE = "SECOND";

    public static final String DRIVE_NAME = "com.mysql.cj.jdbc.Driver";
    public static final String PRIMARY_URL = "jdbc:mysql://localhost/primary";
    public static final String SECOND_URL = "jdbc:mysql://localhost/seconnd";
    public static final String USER_NAME = "root";
    public static final String PASSWORD = "123456";

    public static final NamedDataSource primaryDataSoruce;
    public static final NamedDataSource secondDataSource;

    public static DynamicDataSource dynamicDataSource;

    static {
        primaryDataSoruce = new NamedDataSource(PRIMARY_DATASOURCE, PRIMARY_URL, USER_NAME, PASSWORD, DRIVE_NAME);
        secondDataSource = new NamedDataSource(SECOND_DATASOURCE, SECOND_URL, USER_NAME, PASSWORD, DRIVE_NAME);

        dynamicDataSource = new DynamicDataSource();
    }

    @Test
    public void dynamicDataSourceTest(){
        // 设置默认数据源
        dynamicDataSource.setDefaultTargetDataSource(primaryDataSoruce);

        // 设置目标数据源
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(PRIMARY_DATASOURCE, primaryDataSoruce);
        targetDataSources.put(SECOND_DATASOURCE, secondDataSource);
        dynamicDataSource.setTargetDataSources(targetDataSources);

        // 解析数据源
        dynamicDataSource.afterPropertiesSet();

        selectUserByDataSource(PRIMARY_DATASOURCE);
        selectUserByDataSource(SECOND_DATASOURCE);
    }

    public void selectUserByDataSource(String dataSource){
        DynamicDataSource.setDataSource(dataSource);
        doSelectUser();
    }

    public void doSelectUser(){
        try {
            NamedDataSource currentDataSource = (NamedDataSource) dynamicDataSource.getCurrentDataSource();
            System.out.println("current DataSource is:" + currentDataSource.getDataSourceName());

            Connection connection = currentDataSource.getConnection();
            Statement statement = connection.createStatement();
            ResultSet resultSet = statement.executeQuery("select * from sys_user where user_id = 1");

            while (resultSet.next()){
                System.out.println("password:" + resultSet.getString("password"));
            }

            resultSet.close();
            statement.close();
            connection.close();
        } catch (Exception e){
            e.printStackTrace();
        }
    }

}

class NamedDataSource extends DriverManagerDataSource{

    private final String dataSourceName;

    public NamedDataSource(@NotNull String dataSourceName, String url, String username, String password, String driveName) {
        super(url, username, password);
        super.setDriverClassName(driveName);
        this.dataSourceName = dataSourceName;
    }

    public String getDataSourceName() {
        return dataSourceName;
    }
}

分析

​ AbstractRoutingDataSource作为一个数据源池,其中维护了多个数据源和默认数据源,首先要初始化这些数据源。在每次使用时,通多子类覆盖的setDataSource()方法设置当前需要时候的数据源,然后调用getCurrentDataSource()方法得到数据源,这样就完成了数据源的切换。

​ AbstractRoutingDataSource的部分签名如下:

// 默认数据源
@Nullable
private Object defaultTargetDataSource;

// 目标数据源
@Nullable
private Map<Object, Object> targetDataSources;

// 切换当前的数据源
@Nullable
protected abstract Object determineCurrentLookupKey();

​ 在这其中还涉及到了事物封装的知识点,将在之后进行分析。

整合框架

​ 通过代码实现动态数据源的功能实现了,那个在框架当中,动态数据源是如何实现的呢?

​ 以MyBatis Plus为例,其也是实现了AbstractRoutingDataSource和DynamicRoutingDataSource用为维护多数据源,然后通过工具类DynamicDataSourceContextHolder实现不同数据源之间的切换。

总结

​ 可以看到,动态数据源最主要还是【池】的概念,在池中维护多个数据源,想用哪个可以直接从里面拿。和线程池,数据源池的概念非常类似。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值