动态数据源
动态数据源就是程序在运行过程中,可以在多个数据源之间切换,从而对不同的数据源进行操作。Spring Framework的Jdbc包中,提供了AbstractRoutingDataSource用于实现动态数据源。
实现
可以先自己设计一种动态数据源的实现:在一个数据源池中维护各个不同的数据源,当执行数据库操作时,从数据池中获取对应的数据源链接来进行数据库操作。这样看来我们只需要一个数据源池,而AbstractRoutingDataSource也正是基于这样的原理。
AbstractRoutingDataSource
示例
下面先来看一个示例,创建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实现不同数据源之间的切换。
总结
可以看到,动态数据源最主要还是【池】的概念,在池中维护多个数据源,想用哪个可以直接从里面拿。和线程池,数据源池的概念非常类似。