AbstractRoutingDataSource实现动态多数据源

2022-06-23 11:18:54

动态数据源

动态数据源就是程序在运行过程中,可以在多个数据源之间切换,从而对不同的数据源进行操作。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;publicclassDynamicDataSourceextendsAbstractRoutingDataSource{privatestatic String dataSource;@Overrideprotected ObjectdetermineCurrentLookupKey(){returngetDataSource();}public DataSourcegetCurrentDataSource(){returnsuper.determineTargetDataSource();}publicstatic StringgetDataSource(){return dataSource;}publicstaticvoidsetDataSource(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;publicclassDynamicDataSourceTest{publicstaticfinal String PRIMARY_DATASOURCE="PRIMARY";publicstaticfinal String SECOND_DATASOURCE="SECOND";publicstaticfinal String DRIVE_NAME="com.mysql.cj.jdbc.Driver";publicstaticfinal String PRIMARY_URL="jdbc:mysql://localhost/primary";publicstaticfinal String SECOND_URL="jdbc:mysql://localhost/seconnd";publicstaticfinal String USER_NAME="root";publicstaticfinal String PASSWORD="123456";publicstaticfinal NamedDataSource primaryDataSoruce;publicstaticfinal NamedDataSource secondDataSource;publicstatic DynamicDataSource dynamicDataSource;static{
        primaryDataSoruce=newNamedDataSource(PRIMARY_DATASOURCE, PRIMARY_URL, USER_NAME, PASSWORD, DRIVE_NAME);
        secondDataSource=newNamedDataSource(SECOND_DATASOURCE, SECOND_URL, USER_NAME, PASSWORD, DRIVE_NAME);

        dynamicDataSource=newDynamicDataSource();}@TestpublicvoiddynamicDataSourceTest(){// 设置默认数据源
        dynamicDataSource.setDefaultTargetDataSource(primaryDataSoruce);// 设置目标数据源
        Map<Object, Object> targetDataSources=newHashMap<>();
        targetDataSources.put(PRIMARY_DATASOURCE, primaryDataSoruce);
        targetDataSources.put(SECOND_DATASOURCE, secondDataSource);
        dynamicDataSource.setTargetDataSources(targetDataSources);// 解析数据源
        dynamicDataSource.afterPropertiesSet();selectUserByDataSource(PRIMARY_DATASOURCE);selectUserByDataSource(SECOND_DATASOURCE);}publicvoidselectUserByDataSource(String dataSource){
        DynamicDataSource.setDataSource(dataSource);doSelectUser();}publicvoiddoSelectUser(){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();}}}classNamedDataSourceextendsDriverManagerDataSource{privatefinal String dataSourceName;publicNamedDataSource(@NotNull String dataSourceName, String url, String username, String password, String driveName){super(url, username, password);super.setDriverClassName(driveName);this.dataSourceName= dataSourceName;}public StringgetDataSourceName(){return dataSourceName;}}

分析

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

AbstractRoutingDataSource的部分签名如下:

// 默认数据源@Nullableprivate Object defaultTargetDataSource;// 目标数据源@Nullableprivate Map<Object, Object> targetDataSources;// 切换当前的数据源@Nullableprotectedabstract ObjectdetermineCurrentLookupKey();

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

整合框架

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

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

总结

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

  • 作者:八球
  • 原文链接:https://blog.csdn.net/weixin_50020352/article/details/115819160
    更新时间:2022-06-23 11:18:54