欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

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

首先mybatis-plus使用com.baomidou.dynamic.datasource.AbstractRoutingDataSource繼承 AbstractDataSource接管數(shù)據(jù)源;具體實(shí)現(xiàn)類為com.baomidou.dynamic.datasource.DynamicRoutingDataSource。項(xiàng)目初始化調(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ù)操作時(shí),方法會(huì)被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存儲(chǔ)當(dāng)前線程的數(shù)據(jù)源名。

public final class DynamicDataSourceContextHolder {

? /**
? ?* 為什么要用鏈表存儲(chǔ)(準(zhǔn)確的是棧)
? ?* 為了支持嵌套切換,如ABC三個(gè)service都是不同的數(shù)據(jù)源
? ?* 其中A的某個(gè)業(yè)務(wù)要調(diào)B的方法,B的方法需要調(diào)用C的方法。一級(jí)一級(jí)調(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ù)源
? ?* 如非必要不要手動(dòng)調(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ù)源 只會(huì)移除掉當(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)存泄漏,如手動(dòng)調(diào)用了push可調(diào)用此方法確保清除
? ?*/
? public static void clear() {
? ? LOOKUP_KEY_HOLDER.remove();
? }
}

進(jìn)行數(shù)據(jù)操作時(shí),會(huì)調(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實(shí)現(xiàn),可以看到DynamicRoutingDataSource從DynamicDataSourceContextHolder獲取數(shù)據(jù)源名稱,這個(gè)在之前攔截器處理存進(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í)的數(shù)據(jù)源已經(jīng)切換成了我們需要的數(shù)據(jù)源。

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

附上動(dòng)態(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") 放在類上就是類下所有方法都使用這個(gè)數(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)無法切換連接,會(huì)導(dǎo)致方法報(bào)錯(cuò)。

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

相關(guān)文章

  • MyBatis高級(jí)映射學(xué)習(xí)教程

    MyBatis高級(jí)映射學(xué)習(xí)教程

    這篇文章主要介紹了MyBatis高級(jí)映射學(xué)習(xí)教程的相關(guān)資料,需要的朋友可以參考下
    2016-05-05
  • Spring?Security實(shí)現(xiàn)接口放通的方法詳解

    Spring?Security實(shí)現(xiàn)接口放通的方法詳解

    在用Spring?Security項(xiàng)目開發(fā)中,有時(shí)候需要放通某一個(gè)接口時(shí),我們需要在配置中把接口地址配置上,這樣做有時(shí)候顯得麻煩。本文將通過一個(gè)注解的方式快速實(shí)現(xiàn)接口放通,感興趣的可以了解一下
    2022-05-05
  • 使用@Autowired注解有錯(cuò)誤提示的解決

    使用@Autowired注解有錯(cuò)誤提示的解決

    這篇文章主要介紹了使用@Autowired注解有錯(cuò)誤提示的解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 一個(gè)簡單的類加載器URLClassLoader案例

    一個(gè)簡單的類加載器URLClassLoader案例

    這篇文章主要介紹了一個(gè)簡單的類加載器URLClassLoader案例,這里寫了一個(gè)小 demo,來加深對于反射的理解,需要的朋友可以參考下
    2023-04-04
  • java8新特性之stream的collect實(shí)戰(zhàn)教程

    java8新特性之stream的collect實(shí)戰(zhàn)教程

    這篇文章主要介紹了java8新特性之stream的collect實(shí)戰(zhàn)教程,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • SpringBoot整合Web開發(fā)之文件上傳與@ControllerAdvice

    SpringBoot整合Web開發(fā)之文件上傳與@ControllerAdvice

    @ControllerAdvice注解是Spring3.2中新增的注解,學(xué)名是Controller增強(qiáng)器,作用是給Controller控制器添加統(tǒng)一的操作或處理。對于@ControllerAdvice,我們比較熟知的用法是結(jié)合@ExceptionHandler用于全局異常的處理,但其作用不止于此
    2022-08-08
  • java的幾種定時(shí)器的具體使用(4種)

    java的幾種定時(shí)器的具體使用(4種)

    本文主要介紹了4種類型的定時(shí)器:@Scheduled注解、quartz、new Timer().schedule、使用線程控制的具體使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2021-09-09
  • 詳解Java多線程編程中互斥鎖ReentrantLock類的用法

    詳解Java多線程編程中互斥鎖ReentrantLock類的用法

    Java多線程并發(fā)的程序中使用互斥鎖有synchronized和ReentrantLock兩種方式,這里我們來詳解Java多線程編程中互斥鎖ReentrantLock類的用法:
    2016-07-07
  • idea中解決maven包沖突的問題(maven helper)

    idea中解決maven包沖突的問題(maven helper)

    這篇文章主要介紹了idea中解決maven包沖突的問題(maven helper),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-12-12
  • java如何實(shí)現(xiàn)自動(dòng)生成數(shù)據(jù)庫設(shè)計(jì)文檔

    java如何實(shí)現(xiàn)自動(dòng)生成數(shù)據(jù)庫設(shè)計(jì)文檔

    以前我們還需要手寫數(shù)據(jù)庫設(shè)計(jì)文檔、現(xiàn)在可以通過引入screw核心包來實(shí)現(xiàn)Java?數(shù)據(jù)庫文檔一鍵生成。本文將具體介紹一下如何通過java自動(dòng)生成數(shù)據(jù)庫設(shè)計(jì)文檔,需要的朋友可以參考下
    2021-11-11

最新評論