最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

mybatis-plus @DS實現(xiàn)動態(tài)切換數(shù)據(jù)源原理

 更新時間:2022年07月08日 16:25:45   作者:silver_eagle  
本文主要介紹了mybatis-plus @DS實現(xiàn)動態(tài)切換數(shù)據(jù)源原理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

1、mybatis-plus @DS實現(xiàn)動態(tài)切換數(shù)據(jù)源原理

首先mybatis-plus使用com.baomidou.dynamic.datasource.AbstractRoutingDataSource繼承 AbstractDataSource接管數(shù)據(jù)源;具體實現(xiàn)類為com.baomidou.dynamic.datasource.DynamicRoutingDataSource。項目初始化調(diào)用public synchronized void addDataSource(String ds, DataSource dataSource)加載數(shù)據(jù)源,數(shù)據(jù)源存進(jìn)dataSourceMap中。
private Map<String, DataSource> dataSourceMap = new LinkedHashMap<>();

private Map<String, DynamicGroupDataSource> groupDataSources = new ConcurrentHashMap<>();

public synchronized void addDataSource(String ds, DataSource dataSource) {
? ? if (p6spy) {
? ? ? dataSource = new P6DataSource(dataSource);
? ? }
? ? dataSourceMap.put(ds, dataSource);
? ? if (ds.contains(UNDERLINE)) {
? ? ? String group = ds.split(UNDERLINE)[0];
? ? ? if (groupDataSources.containsKey(group)) {
? ? ? ? groupDataSources.get(group).addDatasource(dataSource);
? ? ? } else {
? ? ? ? try {
? ? ? ? ? DynamicGroupDataSource groupDatasource = new DynamicGroupDataSource(group,
? ? ? ? ? ? ? strategy.newInstance());
? ? ? ? ? groupDatasource.addDatasource(dataSource);
? ? ? ? ? groupDataSources.put(group, groupDatasource);
? ? ? ? } catch (Exception e) {
? ? ? ? ? log.error("dynamic-datasource - add the datasource named [{}] error", ds, e);
? ? ? ? ? dataSourceMap.remove(ds);
? ? ? ? }
? ? ? }
? ? }
? ? log.info("dynamic-datasource - load a datasource named [{}] success", ds);
? }

進(jìn)行數(shù)據(jù)操作時,方法會被com.baomidou.dynamic.datasource.aop.DynamicDataSourceAnnotationInterceptor攔截,

public class DynamicDataSourceAnnotationInterceptor implements MethodInterceptor {

? /**
? ?* The identification of SPEL.
? ?*/
? private static final String DYNAMIC_PREFIX = "#";
? private static final DynamicDataSourceClassResolver RESOLVER = new DynamicDataSourceClassResolver();
? @Setter
? private DsProcessor dsProcessor;

? @Override
? public Object invoke(MethodInvocation invocation) throws Throwable {
? ? try {
? ? ? DynamicDataSourceContextHolder.push(determineDatasource(invocation));
? ? ? return invocation.proceed();
? ? } finally {
? ? ? DynamicDataSourceContextHolder.poll();
? ? }
? }

? private String determineDatasource(MethodInvocation invocation) throws Throwable {
? ? Method method = invocation.getMethod();
? ? DS ds = method.isAnnotationPresent(DS.class)
? ? ? ? ? method.getAnnotation(DS.class)
? ? ? ? : AnnotationUtils.findAnnotation(RESOLVER.targetClass(invocation), DS.class);
? ? String key = ds.value();
? ? return (!key.isEmpty() && key.startsWith(DYNAMIC_PREFIX)) ? dsProcessor
? ? ? ? .determineDatasource(invocation, key) : key;
? }
}

攔截器首先從被攔截的方法或者類(一般@DS注解用于Service,也可用于Mapper和Controller)上尋找@DS注解,獲取到@DS注解的值后將其存入com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;DynamicDataSourceContextHolder使用ThreadLocal存儲當(dāng)前線程的數(shù)據(jù)源名。

public final class DynamicDataSourceContextHolder {

? /**
? ?* 為什么要用鏈表存儲(準(zhǔn)確的是棧)
? ?* 為了支持嵌套切換,如ABC三個service都是不同的數(shù)據(jù)源
? ?* 其中A的某個業(yè)務(wù)要調(diào)B的方法,B的方法需要調(diào)用C的方法。一級一級調(diào)用切換,形成了鏈。
? ?* 傳統(tǒng)的只設(shè)置當(dāng)前線程的方式不能滿足此業(yè)務(wù)需求,必須模擬棧,后進(jìn)先出。
? ?*/
? @SuppressWarnings("unchecked")
? private static final ThreadLocal<Deque<String>> LOOKUP_KEY_HOLDER = new ThreadLocal() {
? ? @Override
? ? protected Object initialValue() {
? ? ? return new ArrayDeque();
? ? }
? };

? private DynamicDataSourceContextHolder() {
? }

? /**
? ?* 獲得當(dāng)前線程數(shù)據(jù)源
? ?* @return 數(shù)據(jù)源名稱
? ?*/
? public static String peek() {
? ? return LOOKUP_KEY_HOLDER.get().peek();
? }

? /**
? ?* 設(shè)置當(dāng)前線程數(shù)據(jù)源
? ?* 如非必要不要手動調(diào)用,調(diào)用后確保最終清除
? ?* @param ds 數(shù)據(jù)源名稱
? ?*/
? public static void push(String ds) {
? ? LOOKUP_KEY_HOLDER.get().push(StringUtils.isEmpty(ds) ? "" : ds);
? }

? /**
? ?* 清空當(dāng)前線程數(shù)據(jù)源
? ?* 如果當(dāng)前線程是連續(xù)切換數(shù)據(jù)源 只會移除掉當(dāng)前線程的數(shù)據(jù)源名稱
? ?*/
? public static void poll() {
? ? Deque<String> deque = LOOKUP_KEY_HOLDER.get();
? ? deque.poll();
? ? if (deque.isEmpty()) {
? ? ? LOOKUP_KEY_HOLDER.remove();
? ? }
? }

? /**
? ?* 強(qiáng)制清空本地線程
? ?* 防止內(nèi)存泄漏,如手動調(diào)用了push可調(diào)用此方法確保清除
? ?*/
? public static void clear() {
? ? LOOKUP_KEY_HOLDER.remove();
? }
}

進(jìn)行數(shù)據(jù)操作時,會調(diào)用org.springframework.jdbc.datasource.getConnection()方法;getConnection()方法最終調(diào)用了com.baomidou.dynamic.datasource.AbstractRoutingDataSource的getConnection()方法;

  @Override
  public Connection getConnection() throws SQLException {
    return determineDataSource().getConnection();
  }

determineDataSource()由子類com.baomidou.dynamic.datasource.DynamicRoutingDataSource實現(xiàn),可以看到DynamicRoutingDataSource從DynamicDataSourceContextHolder獲取數(shù)據(jù)源名稱,這個在之前攔截器處理存進(jìn)ThreadLocal中,如果有數(shù)據(jù)源名稱則從dataSourceMap中獲取,沒有則獲取默認(rèn)的primary數(shù)據(jù)源。

public DataSource determineDataSource() {
? ? return getDataSource(DynamicDataSourceContextHolder.peek());
}

public DataSource getDataSource(String ds) {
? ? if (StringUtils.isEmpty(ds)) {
? ? ? ? return determinePrimaryDataSource();
? ? } else if (!groupDataSources.isEmpty() && groupDataSources.containsKey(ds)) {
? ? ? ? log.debug("dynamic-datasource switch to the datasource named [{}]", ds);
? ? ? ? return groupDataSources.get(ds).determineDataSource();
? ? } else if (dataSourceMap.containsKey(ds)) {
? ? ? ? log.debug("dynamic-datasource switch to the datasource named [{}]", ds);
? ? ? ? return dataSourceMap.get(ds);
? ? }
? ? if (strict) {
? ? ? ? throw new RuntimeException("dynamic-datasource could not find a datasource named" + ds);
? ? }
? ? return determinePrimaryDataSource();
}

private DataSource determinePrimaryDataSource() {
? ? log.debug("dynamic-datasource switch to the primary datasource");
? ? return groupDataSources.containsKey(primary) ? groupDataSources.get(primary)
? ? ? ? .determineDataSource() : dataSourceMap.get(primary);
}

此時的數(shù)據(jù)源已經(jīng)切換成了我們需要的數(shù)據(jù)源。

數(shù)據(jù)操作完成后,方法返回第二步中的攔截器,執(zhí)行DynamicDataSourceContextHolder.poll();清除掉此次的數(shù)據(jù)源,避免影響后續(xù)數(shù)據(jù)操作。

附上動態(tài)數(shù)據(jù)源相關(guān)配置

spring:
  application:
    name: 
  datasource:
    dynamic:
      primary: dataSource1
      datasource:
        dataSource1:
          type: com.alibaba.druid.pool.DruidDataSource
          driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
          url: jdbc:sqlserver://localhost:1433;database=dataSource1
          username: 
          password: 
        dataSource2:
          type: com.alibaba.druid.pool.DruidDataSource
          driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
          url: jdbc:sqlserver://localhost:1433;instanceName=sqlserver2017;DatabaseName=dataSource2
          username: 
          password: 

pom.xml

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>2.5.6</version>
</dependency>

相應(yīng)類

@Service
//@DS("dataSource2") 放在類上就是類下所有方法都使用這個數(shù)據(jù)源。
public class XXXServiceImpl extends BaseServiceImpl<XXXMapper, XXXBean> implements XXXService {

? ? @DS("dataSource1")
? ? public void selectDataFromSource1() {
? ? ? ?// do somethinng;
? ? }
? ??
? ? @DS("dataSource2")
? ? public void selectDataFromSource1() {
? ? ? ?// do somethinng;
? ? }
}

**注意:**不可在事務(wù)中切換數(shù)據(jù)庫,保證事務(wù)需要方法使用同一連接,使用@DS(dataSource1)方法調(diào)用@DS(dataSource2)無法切換連接,會導(dǎo)致方法報錯。

到此這篇關(guān)于mybatis-plus @DS實現(xiàn)動態(tài)切換數(shù)據(jù)源原理的文章就介紹到這了,更多相關(guān)mybatis-plus @DS動態(tài)切換數(shù)據(jù)源內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot多種自定義錯誤頁面方式小結(jié)

    SpringBoot多種自定義錯誤頁面方式小結(jié)

    這篇文章主要介紹了SpringBoot多種自定義錯誤頁面方式小結(jié),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 淺談JMeter engine啟動原理

    淺談JMeter engine啟動原理

    本文主要介紹jmeter在控制臺在點擊執(zhí)行之后底層所做的一些主要事情及內(nèi)容,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • SpringBoot+logback默認(rèn)日志的配置和使用方式

    SpringBoot+logback默認(rèn)日志的配置和使用方式

    這篇文章主要介紹了SpringBoot+logback默認(rèn)日志的配置和使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • 使用Spring組合自定義的注釋 mscharhag操作

    使用Spring組合自定義的注釋 mscharhag操作

    這篇文章主要介紹了使用Spring組合自定義的注釋 mscharhag,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的工作或?qū)W習(xí)有一定的參考借鑒價值,需要的朋友可以參考下
    2020-03-03
  • 關(guān)于fastjson的@JSONField注解的一些問題(詳解)

    關(guān)于fastjson的@JSONField注解的一些問題(詳解)

    下面小編就為大家?guī)硪黄P(guān)于fastjson的@JSONField注解的一些問題(詳解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-02-02
  • 深入理解HashMap各個方法的源碼

    深入理解HashMap各個方法的源碼

    這篇文章主要介紹了深入理解HashMap各個方法的源碼,HashMap初始容量不能為負(fù)數(shù),若初始容量大于最大容量,則讓它等于最大容量,負(fù)載因子必須大于0,并且傳入的initialCapacity不是HashMap的容量大小,需要的朋友可以參考下
    2023-12-12
  • Java 如何遍歷JsonObject對象

    Java 如何遍歷JsonObject對象

    這篇文章主要介紹了Java 如何遍歷JsonObject對象?今天小編就為大家分享一篇Java遍歷JsonObject對象案例,希望對大家有所幫助吧
    2021-01-01
  • SpringMVC Controller解析ajax參數(shù)過程詳解

    SpringMVC Controller解析ajax參數(shù)過程詳解

    這篇文章主要介紹了SpringMVC Controller解析ajax參數(shù)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-07-07
  • java 文件的操作Path、Paths、Files詳解

    java 文件的操作Path、Paths、Files詳解

    Java NIO(New I/O)是Java 7中引入的一項重要特性,旨在提供一種更加靈活和高效的文件處理方式,NIO.2主要通過Path、Paths和Files三個核心組件來實現(xiàn)對文件和目錄的操作,本文給大家介紹java 文件的操作Path、Paths、Files的相關(guān)知識,感興趣的朋友一起看看吧
    2024-10-10
  • Dwr3.0純注解(純Java Code配置)配置與應(yīng)用淺析二之前端調(diào)用后端

    Dwr3.0純注解(純Java Code配置)配置與應(yīng)用淺析二之前端調(diào)用后端

    我們講到了后端純Java Code的Dwr3配置,完全去掉了dwr.xml配置文件,但是對于使用注解的類卻沒有使用包掃描,而是在Servlet初始化參數(shù)的classes里面加入了我們的Service組件的聲明暴露,對于這個問題需要后面我們再細(xì)細(xì)研究下這篇文章,主要分析介紹前端怎么直接調(diào)用后端
    2016-04-04

最新評論

四子王旗| 伊宁市| 博白县| 陆丰市| 周口市| 扎赉特旗| 丹巴县| 田东县| 改则县| 县级市| 吉木乃县| 钦州市| 余姚市| 万宁市| 克东县| 方城县| 通江县| 离岛区| 富川| 台安县| 寻乌县| 肇庆市| 湟中县| 博乐市| 丁青县| 宜良县| 台江县| 福清市| 北安市| 德令哈市| 越西县| 栖霞市| 鸡泽县| 汉中市| 辰溪县| 娄烦县| 安龙县| 永新县| 三台县| 尚义县| 莱芜市|