Spring中的動態(tài)數(shù)據(jù)源解讀
動態(tài)數(shù)據(jù)源的原理得先說清。
原理
平常在使用Mysql的時候是通過JDBC的,得給一個url,userName,和password,如下:
jdbc:mysql://localhost:3306/t_db1?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
一個url對應(yīng)一個Connection對象,需要在url中指定需要連接的庫。
之后的Mysql的CRUD的操作都是通過Connection對象來做的。所以,動態(tài),就是在這個時候操作的,在獲取Connection的時候來通過指定的key來判斷要用哪個數(shù)據(jù)源。這得有一個前提,得先建立Connection,之后在獲取Connection的時候在判斷,
1.通過什么判斷呢?存放key和Connection的數(shù)據(jù)結(jié)構(gòu)是什么?
只要指定一個回調(diào)方法,key隨便,存放key和Connection的數(shù)據(jù)結(jié)構(gòu)是Map。在獲取Connection的時候,先確定Key,在獲取Connection就好了。
再看Spring
Java連接Mysql指定了接口,需要實現(xiàn)DataSource
接口,Spring中提供了抽象類(AbstractDataSource
)
解釋說明
DriverManagerDataSource
這個沒有什么可說,只是封裝了一下連接mysql需要用的一些屬性,比如userName,password,url,driver,就是就老一套的獲取Connection封裝了一下
AbstractRoutingDataSource
Spring提供的動態(tài)數(shù)據(jù)源的抽象類
首先,它里面有一個Map,Map的key是Object(這是自定義的),V是Object(雖然是Object,代碼里面規(guī)定了,只能為String,和DataSource),其實它本質(zhì)就應(yīng)該是DataSource,這里是String的目的是為了可以通過DataSourceLookup
來獲取數(shù)據(jù)源。(比如,可以在配置數(shù)據(jù)源的時候,直接配置數(shù)據(jù)源的bean的名字,然后自己寫一個DataSourceLookup的實現(xiàn)類,從BeanFactory中獲取DataSource)。
還提供了一個determineCurrentLookupKey()
方法來判斷當(dāng)前數(shù)據(jù)源的key,其實就是map中的key。在最終獲取Connection的時候通過當(dāng)前的key獲取DataSource,在通過DataSource獲取真正的Connection。
代碼分析
1.屬性
2.實現(xiàn)接口
實現(xiàn)了InitializingBean那它肯定在afterPropertiesSet
會做操作。等會看看
3.重要方法
afterPropertiesSet
獲取Connection之前確定Key
`determineCurrentLookupKey`方法是留給子類拓展的。
determineCurrentLookupKey怎么來確定key呢?
它是一個無參的方法,一般來說,都是放在ThreadLocal中,在執(zhí)行sql操作之前,在對應(yīng)的ThreadLocal放這次需要的key,就可以在determineCurrentLookupKey中獲取ThreadLocal中的值,確定key。
<font color='red'>Spring提供了一個動態(tài)數(shù)據(jù)源的實現(xiàn)類,`IsolationLevelDataSourceRouter`,key是事務(wù)的隔離級別。意思就是可以根據(jù)不同的隔離級別來選擇DataSource</font>
還有,一般都是用切面來操作ThreadLocal的
例子
我這里為了簡單,就不寫切面了。直接代碼cv。
1.配置類
package datasource.dynamic; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DriverManagerDataSource; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import javax.sql.DataSource; import java.util.HashMap; import java.util.Map; @Configuration public class Config { @Bean public DriverManagerDataSource dataSource1(){ String url = "jdbc:mysql://localhost:3306/t_db1?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"; String userName = "root"; String password = "123456"; return new DriverManagerDataSource(url,userName,password); } @Bean public DriverManagerDataSource dataSource2(){ String url = "jdbc:mysql://localhost:3306/t_db2?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"; String userName = "root"; String password = "123456"; return new DriverManagerDataSource(url,userName,password); } @Bean public DriverManagerDataSource dataSource3(){ String url = "jdbc:mysql://localhost:3306/t_db3?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC"; String userName = "root"; String password = "123456"; return new DriverManagerDataSource(url,userName,password); } @Bean public MyDynamicDataSource dynamicDataSource(Map<String,DriverManagerDataSource> map){ HashMap<Object, Object> original = new HashMap<>(map); MyDynamicDataSource myDynamicDataSource = new MyDynamicDataSource(); myDynamicDataSource.setTargetDataSources(original); return myDynamicDataSource; } @Bean public JdbcTemplate jdbcTemplate(AbstractRoutingDataSource dataSource){ return new JdbcTemplate(dataSource); } }
配置了三個數(shù)據(jù)庫,也就是三個數(shù)據(jù)源,利用JdbcTemplate來快捷的執(zhí)行sql,JdbcTemplate需要指定用自己定義的動態(tài)數(shù)據(jù)源(MyDynamicDataSource)。
此外,之前說了,動態(tài)數(shù)據(jù)源里面有個map,它得需要數(shù)據(jù)源,此外還需要key,這里用bean的名字做為key,創(chuàng)建MyDynamicDataSource之后,設(shè)置TargetDataSources。當(dāng)MyDynamicDataSource被Spring創(chuàng)建的時候,InitializingBean#afterPropertiesSet()
會通過DataSourceLookup轉(zhuǎn)換。
2.ThreadLocal
package datasource.dynamic; import org.springframework.util.Assert; public class DataSourceKeyHolder { private static final ThreadLocal<String> keyHolder = new ThreadLocal<>(); public static void setKey(String key) { keyHolder.remove(); keyHolder.set(key); } public static String getKey() { String s = keyHolder.get(); Assert.notNull(s, "key 不能為空"); return s; } public static void clear(){ keyHolder.remove(); } }
3.動態(tài)數(shù)據(jù)源實現(xiàn)類
package datasource.dynamic; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class MyDynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceKeyHolder.getKey(); } }
只是從ThreadLocal中獲取當(dāng)前的key就好了。
4.實體對象
public class TestBean { private Integer id; private String name; private Double age; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Double getAge() { return age; } public void setAge(Double age) { this.age = age; } @Override public String toString() { return "TestBean{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}'; } }
5.sql
-- auto-generated definition create table t_test ( id int auto_increment primary key, name text null, age double null );
創(chuàng)建三個數(shù)據(jù)庫,在三個數(shù)據(jù)庫里面都創(chuàng)建這個表,填寫點數(shù)據(jù)。
6.主要測試類
package datasource.dynamic; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; public class MainTest { public static void main(String[] args) { try { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class); JdbcTemplate jdbcTemplate = context.getBean(JdbcTemplate.class); // 這里從頭到尾用的都是一個JdbcTemplate dataSource1(jdbcTemplate); // 動態(tài)數(shù)據(jù)源1 dataSource2(jdbcTemplate);// 動態(tài)數(shù)據(jù)源2 dataSource3(jdbcTemplate);// 動態(tài)數(shù)據(jù)源3 }catch (Exception e){ e.printStackTrace(); } } public static void dataSource1( JdbcTemplate jdbcTemplate ){ DataSourceKeyHolder.setKey("dataSource1"); try { TestBean testBean = jdbcTemplate.queryForObject("select * from t_test where id=?", new BeanPropertyRowMapper<>(TestBean.class), 1); System.out.println(testBean); }finally { DataSourceKeyHolder.clear(); } } public static void dataSource2( JdbcTemplate jdbcTemplate ){ DataSourceKeyHolder.setKey("dataSource2"); try { TestBean testBean = jdbcTemplate.queryForObject("select * from t_test where id=?", new BeanPropertyRowMapper<>(TestBean.class), 1); System.out.println(testBean); }finally { DataSourceKeyHolder.clear(); } } public static void dataSource3( JdbcTemplate jdbcTemplate ){ DataSourceKeyHolder.setKey("dataSource3"); try { TestBean testBean = jdbcTemplate.queryForObject("select * from t_test where id=?", new BeanPropertyRowMapper<>(TestBean.class), 1); System.out.println(testBean); }finally { DataSourceKeyHolder.clear(); } } }
7.結(jié)果
總結(jié)
關(guān)于這篇文章,我是把它當(dāng)做我的筆記,里面有很多的內(nèi)容反映了我思考的過程,因為思維有限,不免有些內(nèi)容有出入,如果有問題,歡迎指出。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
- Spring實現(xiàn)動態(tài)數(shù)據(jù)源切換的方法總結(jié)
- 解讀動態(tài)數(shù)據(jù)源dynamic-datasource-spring-boot-starter使用問題
- Springboot實現(xiàn)根據(jù)用戶ID切換動態(tài)數(shù)據(jù)源
- 如何在Java SpringBoot項目中配置動態(tài)數(shù)據(jù)源你知道嗎
- Spring AbstractRoutingDatasource 動態(tài)數(shù)據(jù)源的實例講解
- 詳解SpringBoot+Mybatis實現(xiàn)動態(tài)數(shù)據(jù)源切換
相關(guān)文章
Java單例模式實現(xiàn)靜態(tài)內(nèi)部類方法示例
這篇文章主要介紹了Java單例模式實現(xiàn)靜態(tài)內(nèi)部類方法示例,涉及構(gòu)造函數(shù)私有化等相關(guān)內(nèi)容,需要的朋友可以了解下。2017-09-09Java中ClassLoader類加載學(xué)習(xí)總結(jié)
本篇文章主要給大家講述了Java中ClassLoader類加載的原理以及用法總結(jié),一起學(xué)習(xí)下。2017-12-12IDEA 當(dāng)前在線人數(shù)和歷史訪問量的示例代碼
這篇文章主要介紹了IDEA 當(dāng)前在線人數(shù)和歷史訪問量的實例代碼,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08關(guān)于SpringBoot改動后0.03秒啟動的問題
這篇文章主要介紹了SpringBoot改動后0.03秒啟動,本文結(jié)合示例代碼給大家講解的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-12-12