Springboot動態(tài)切換數(shù)據(jù)源的具體實現(xiàn)與原理分析
前言
在springboot項目中只需一句代碼即可實現(xiàn)多個數(shù)據(jù)源之間的切換:
// 切換sqlserver數(shù)據(jù)源: DataSourceContextHolder.setDataBaseType(DataSourceEnum.SQLSERVER_DATASOURCE); ...... // 切換mysql數(shù)據(jù)源 DataSourceContextHolder.setDataBaseType(DataSourceEnum.MYSQL_DATASOURCE);
具體實現(xiàn):
本實例基于springboot2.5+版本實現(xiàn)。
1.配置數(shù)據(jù)源:
在配置文件中配置多個數(shù)據(jù)源的連接信息,用不同的前綴作為區(qū)別:
# sqlserver數(shù)據(jù)源1:前綴為:spring.datasource.sqlserver spring.datasource.sqlserver.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver spring.datasource.sqlserver.jdbc-url=jdbc:sqlserver://localhost:1433;DatabaseName=test spring.datasource.sqlserver.username=sa spring.datasource.sqlserver.password=sa # mysql數(shù)據(jù)源1:前綴為:spring.datasource.mysql spring.datasource.mysql.driver-class-name=com.mysql.jdbc.Driver spring.datasource.mysql.jdbc-url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowMultiQueries=true spring.datasource.mysql.username=root spring.datasource.mysql.password=root # sqlLite數(shù)據(jù)源1:前綴為:spring.datasource.sqlite spring.datasource.sqlite.driver-class-name=org.sqlite.JDBC spring.datasource.sqlite.jdbc-url=jdbc:sqlite:D://sqllite//test.db spring.datasource.sqlite.username= spring.datasource.sqlite.password= # sqlserver數(shù)據(jù)源2:前綴為:spring.datasource.sqlserver2 spring.datasource.sqlserver2.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver spring.datasource.sqlserver2.jdbc-url=jdbc:sqlserver://localhost;DatabaseName=test1 spring.datasource.sqlserver2.username=sa spring.datasource.sqlserver2.password=sa # 配置數(shù)據(jù)庫連接池信息 spring.datasource.hikari.maximum-pool-size=32 spring.datasource.hikari.minimum-idle=16
2.新建枚舉類DataSourceEnum,有幾個數(shù)據(jù)源對應設置幾個枚舉類。
public enum DataSourceEnum { MYSQL_DATASOURCE, SQLSERVER_DATASOURCE, SQLSERVER2_DATASOURCE, SQLLITE_DATASOURCE }
3.新建數(shù)據(jù)庫切換工具類DataSourceContextHolder,這里通過ThreadLocal類型的變量來存儲當前數(shù)據(jù)源枚舉類,同時能夠保證線程安全。
public class DataSourceContextHolder { /** * 通過ThreadLocal保證線程安全 */ private static final ThreadLocal<DataSourceEnum> contextHolder = new ThreadLocal<>(); /** * 設置數(shù)據(jù)源變量 * @param dataSourceEnum 數(shù)據(jù)源變量 */ public static void setDataBaseType(DataSourceEnum dataSourceEnum) { System.out.println("修改數(shù)據(jù)源為:" + dataSourceEnum); contextHolder.set(dataSourceEnum); } /** * 獲取數(shù)據(jù)源變量 * @return 數(shù)據(jù)源變量 */ public static DataSourceEnum getDataBaseType() { DataSourceEnum dataSourceEnum = contextHolder.get() == null ? DataSourceEnum.MYSQL_DATASOURCE : contextHolder.get(); System.out.println("當前數(shù)據(jù)源的類型為:" + dataSourceEnum); return dataSourceEnum; } /** * 清空數(shù)據(jù)類型 */ public static void clearDataBaseType() { contextHolder.remove(); }
4.新建DynamicDataSource類繼承AbstractRoutingDataSource類,并實現(xiàn)determineCurrentLookupKey方法,該方法是指定當前默認數(shù)據(jù)源的方法。
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataBaseType(); } }
這個類看似內容不多,但其實繼承了AbstractRoutingDataSource類是實現(xiàn)動態(tài)切換數(shù)據(jù)源的關鍵。
5.新建DataSourceConfig類用來創(chuàng)建bean的實例,其中包括各數(shù)據(jù)源的DataSource實例,DynamicDataSource實例以及跟Mybatis相關的SqlSessionFactory或Spring的JdbcTemplate實例。
@Configuration public class DataSourceConfig { @Bean(name = "sqlserverDataSource") @ConfigurationProperties(prefix = "spring.datasource.sqlserver") public DataSource getDateSource1() { return DataSourceBuilder.create().build(); } @Bean(name = "sqlserver2DataSource") @ConfigurationProperties(prefix = "spring.datasource.sqlserver2") public DataSource getDateSource11() { return DataSourceBuilder.create().build(); } @Bean(name = "mysqlDataSource") @ConfigurationProperties(prefix = "spring.datasource.mysql") public DataSource getDateSource2() { return DataSourceBuilder.create().build(); } @Bean(name = "sqlLiteDataSource") @ConfigurationProperties(prefix = "spring.datasource.sqlite") public DataSource getDateSource3() { return DataSourceBuilder.create().build(); } @Bean(name = "dynamicDataSource") public DynamicDataSource DataSource(@Qualifier("sqlserverDataSource") DataSource sqlserverDataSource, @Qualifier("sqlserver2DataSource") DataSource sqlserver2DataSource, @Qualifier("mysqlDataSource") DataSource mysqlDataSource, @Qualifier("sqlLiteDataSource") DataSource sqlLiteDataSource) { //配置多數(shù)據(jù)源 Map<Object, Object> targetDataSource = new HashMap<>(); targetDataSource.put(DataSourceEnum.SQLSERVER_DATASOURCE, sqlserverDataSource); targetDataSource.put(DataSourceEnum.MYSQL_DATASOURCE, mysqlDataSource); targetDataSource.put(DataSourceEnum.SQLLITE_DATASOURCE, sqlLiteDataSource); targetDataSource.put(DataSourceEnum.SQLSERVER2_DATASOURCE, sqlserver2DataSource); DynamicDataSource dataSource = new DynamicDataSource(); //多數(shù)據(jù)源 dataSource.setTargetDataSources(targetDataSource); //默認數(shù)據(jù)源 dataSource.setDefaultTargetDataSource(sqlserverDataSource); return dataSource; } @Bean(name = "SqlSessionFactory") public SqlSessionFactory test1SqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dynamicDataSource); return bean.getObject(); } @Bean(name = "JdbcTemplate") public JdbcTemplate test1JdbcTemplate(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) { return new JdbcTemplate(dynamicDataSource); } }
這樣就把我們切換數(shù)據(jù)庫鎖需要的bean全部交給spring容器中了,使用時直接通過DataSourceContextHolder.setDataBaseType(DataSourceEnum dataSourceEnum);這個方法指定數(shù)據(jù)源對應的枚舉類即可。
原理分析:
其實我們新建數(shù)據(jù)庫連接的時候也是通過DataSource來獲取連接的,這里的AbstractRoutingDataSource也是通過了DataSource中的getConnection方法來獲取連接的。
這個類里維護了兩個Map來存儲數(shù)據(jù)庫連接信息:
@Nullable private Map<Object, Object> targetDataSources; @Nullable private Object defaultTargetDataSource; private boolean lenientFallback = true; private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup(); @Nullable private Map<Object, DataSource> resolvedDataSources; @Nullable private DataSource resolvedDefaultDataSource;
下面對上面的幾個屬性進行說明:
其中第一個targetDataSources是一個Map對象,在我們上面第五步創(chuàng)建DynamicDataSource實例的時候將多個數(shù)據(jù)源的DataSource類,放入到這個Map中去,這里的Key是枚舉類,values就是DataSource類。
第二個defaultTargetDataSource是默認的數(shù)據(jù)源,就是DynamicDataSource中唯一重寫的方法來給這個對象賦值的。
第三個lenientFallback是一個標識,是當指定數(shù)據(jù)源不存在的時候是否采用默認數(shù)據(jù)源,默認是true,設置為false之后如果找不到指定數(shù)據(jù)源將會返回null.
第四個dataSourceLookup是用來解析指定的數(shù)據(jù)源對象為DataSource實例的。默認是JndiDataSourceLookup實例,繼承自DataSourceLookup接口。
第五個resolvedDataSources也是一個Map對象,這里是存放指定數(shù)據(jù)源解析后的DataSource對象。
第六個resolvedDefaultDataSource是默認的解析后的DataSource數(shù)據(jù)源對象上面的getConnection方法就是從這個變量中拿到DataSource實例并獲取連接的。
總結
- springboot集成@DS注解實現(xiàn)數(shù)據(jù)源切換的方法示例
- springboot+dynamicDataSource動態(tài)添加切換數(shù)據(jù)源方式
- SpringBoot?+DynamicDataSource切換多數(shù)據(jù)源的全過程
- Springboot實現(xiàn)根據(jù)用戶ID切換動態(tài)數(shù)據(jù)源
- 詳細聊聊SpringBoot中動態(tài)切換數(shù)據(jù)源的方法
- Spring配置多數(shù)據(jù)源切換
- SpringBoot AOP方式實現(xiàn)多數(shù)據(jù)源切換的方法
- Spring配置多個數(shù)據(jù)源并實現(xiàn)數(shù)據(jù)源的動態(tài)切換功能
相關文章
Java 獲取當前時間及實現(xiàn)時間倒計時功能【推薦】
這篇文章主要介紹了Java 獲取當前時間及實現(xiàn)時間倒計時功能 ,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-05-05SpringMvc配置靜態(tài)資源訪問路徑的實現(xiàn)
本文主要介紹了SpringMvc配置靜態(tài)資源訪問路徑的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-07-07詳解基于java的Socket聊天程序——服務端(附demo)
這篇文章主要介紹了詳解基于java的Socket聊天程序——服務端(附demo),具有一定的參考價值,感興趣的小伙伴們可以參考一下。2016-12-12