<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring Boot 優(yōu)雅配置多數(shù)據(jù)源

          共 12792字,需瀏覽 26分鐘

           ·

          2022-05-21 12:33

          大約在19年的這個時候,老同事公司在做醫(yī)療系統(tǒng),需要和HIS系統(tǒng)對接一些信息,比如患者、醫(yī)護、醫(yī)囑、科室等信息。但是起初并不知道如何與HIS無縫對接,于是向我取經(jīng)。

          最終經(jīng)過討論采用了視圖對接的方式,大致就是HIS系統(tǒng)提供視圖,他們進行對接。

          什么是多數(shù)據(jù)源?

          最常見的單一應用中最多涉及到一個數(shù)據(jù)庫,即是一個數(shù)據(jù)源(Datasource)。那么顧名思義,多數(shù)據(jù)源就是在一個單一應用中涉及到了兩個及以上的數(shù)據(jù)庫了。

          其實在配置數(shù)據(jù)源的時候就已經(jīng)很明確這個定義了,如以下代碼:

          ????@Bean(name?=?"dataSource")
          ????public?DataSource?dataSource()?{
          ????????DruidDataSource?druidDataSource?=?new?DruidDataSource();
          ????????druidDataSource.setUrl(url);
          ????????druidDataSource.setUsername(username);
          ????????druidDataSource.setDriverClassName(driverClassName);
          ????????druidDataSource.setPassword(password);
          ????????return?druidDataSource;
          ????}

          urlusernamepassword這三個屬性已經(jīng)唯一確定了一個數(shù)據(jù)庫了,DataSource則是依賴這三個創(chuàng)建出來的。則多數(shù)據(jù)源即是配置多個DataSource(暫且這么理解)。

          何時用到多數(shù)據(jù)源?

          正如前言介紹到的一個場景,相信大多數(shù)做過醫(yī)療系統(tǒng)的都會和HIS打交道,為了簡化護士以及醫(yī)生的操作流程,必須要將必要的信息從HIS系統(tǒng)對接過來,據(jù)我了解的大致有兩種方案如下:

          1. HIS提供視圖,比如醫(yī)護視圖、患者視圖等,而此時其他系統(tǒng)只需要定時的從HIS視圖中讀取數(shù)據(jù)同步到自己數(shù)據(jù)庫中即可。
          2. HIS提供接口,無論是webService還是HTTP形式都是可行的,此時其他系統(tǒng)只需要按照要求調(diào)接口即可。

          很明顯第一種方案涉及到了至少兩個數(shù)據(jù)庫了,一個是HIS數(shù)據(jù)庫,一個自己系統(tǒng)的數(shù)據(jù)庫,在單一應用中必然需要用到多數(shù)據(jù)源的切換才能達到目的。

          當然多數(shù)據(jù)源的使用場景還是有很多的,以上只是簡單的一個場景。

          整合單一的數(shù)據(jù)源

          本文使用阿里的數(shù)據(jù)庫連接池druid,添加依賴如下:


          <dependency>
          ???<groupId>com.alibabagroupId>
          ???<artifactId>druid-spring-boot-starterartifactId>
          ???<version>1.1.9version>
          dependency>

          阿里的數(shù)據(jù)庫連接池非常強大,比如數(shù)據(jù)監(jiān)控數(shù)據(jù)庫加密等等內(nèi)容,本文僅僅演示與Spring Boot整合的過程,一些其他的功能后續(xù)可以自己研究添加。

          Druid連接池的starter的自動配置類是DruidDataSourceAutoConfigure,類上標注如下一行注解:

          @EnableConfigurationProperties({DruidStatProperties.class,?DataSourceProperties.class})

          @EnableConfigurationProperties這個注解使得配置文件中的配置生效并且映射到指定類的屬性。

          DruidStatProperties中指定的前綴是spring.datasource.druid,這個配置主要是用來設(shè)置連接池的一些參數(shù)。

          DataSourceProperties中指定的前綴是spring.datasource,這個主要是用來設(shè)置數(shù)據(jù)庫的urlusernamepassword等信息。

          因此我們只需要在全局配置文件中指定數(shù)據(jù)庫的一些配置以及連接池的一些配置信息即可,前綴分別是spring.datasource.druidspring.datasource,以下是個人隨便配置的(application.properties):

          spring.datasource.url=jdbc\:mysql\://120.26.101.xxx\:3306/xxx?useUnicode\=true&characterEncoding\=UTF-8&zeroDateTimeBehavior\=convertToNull&useSSL\=false&allowMultiQueries\=true&serverTimezone=Asia/Shanghai
          spring.datasource.username=root
          spring.datasource.password=xxxx
          spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
          spring.datasource.driver-class-name=com.mysql.jdbc.Driver
          #初始化連接大小
          spring.datasource.druid.initial-size=0
          #連接池最大使用連接數(shù)量
          spring.datasource.druid.max-active=20
          #連接池最小空閑
          spring.datasource.druid.min-idle=0
          #獲取連接最大等待時間
          spring.datasource.druid.max-wait=6000
          spring.datasource.druid.validation-query=SELECT 1
          #spring.datasource.druid.validation-query-timeout=6000
          spring.datasource.druid.test-on-borrow=false
          spring.datasource.druid.test-on-return=false
          spring.datasource.druid.test-while-idle=true
          #配置間隔多久才進行一次檢測,檢測需要關(guān)閉的空閑連接,單位是毫秒
          spring.datasource.druid.time-between-eviction-runs-millis=60000
          #置一個連接在池中最小生存的時間,單位是毫秒
          spring.datasource.druid.min-evictable-idle-time-millis=25200000
          #spring.datasource.druid.max-evictable-idle-time-millis=
          #打開removeAbandoned功能,多少時間內(nèi)必須關(guān)閉連接
          spring.datasource.druid.removeAbandoned=true
          #1800秒,也就是30分鐘
          spring.datasource.druid.remove-abandoned-timeout=1800
          #
          spring.datasource.druid.log-abandoned=true
          spring.datasource.druid.filters=mergeStat

          在全局配置文件application.properties文件中配置以上的信息即可注入一個數(shù)據(jù)源到Spring Boot中。其實這僅僅是一種方式,下面介紹另外一種方式。

          在自動配置類中DruidDataSourceAutoConfigure中有如下一段代碼:

          ??@Bean(initMethod?=?"init")
          ????@ConditionalOnMissingBean
          ????public?DataSource?dataSource()?{
          ????????LOGGER.info("Init?DruidDataSource");
          ????????return?new?DruidDataSourceWrapper();
          ????}

          @ConditionalOnMissingBean@Bean這兩個注解的結(jié)合,意味著我們可以覆蓋,只需要提前在IOC中注入一個DataSource類型的Bean即可。

          因此我們在自定義的配置類中定義如下配置即可:

          /**
          ?????*?@Bean:向IOC容器中注入一個Bean
          ?????*?@ConfigurationProperties:使得配置文件中以spring.datasource為前綴的屬性映射到Bean的屬性中
          ?????*?@return
          ?????*/

          ????@ConfigurationProperties(prefix?=?"spring.datasource")
          ????@Bean
          ????public?DataSource?dataSource(){
          ????????//做一些其他的自定義配置,比如密碼加密等......
          ????????return?new?DruidDataSource();
          ????}

          以上介紹了兩種數(shù)據(jù)源的配置方式,第一種比較簡單,第二種適合擴展,按需選擇。

          整合Mybatis

          Spring Boot 整合Mybatis其實很簡單,簡單的幾步就搞定,首先添加依賴:

          <dependency>
          ?????<groupId>org.mybatis.spring.bootgroupId>
          ?????<artifactId>mybatis-spring-boot-starterartifactId>
          ?????<version>2.0.0version>
          dependency>

          第二步找到自動配置類MybatisAutoConfiguration,有如下一行代碼:

          @EnableConfigurationProperties(MybatisProperties.class)

          老套路了,全局配置文件中配置前綴為mybatis的配置將會映射到該類中的屬性。

          可配置的東西很多,比如XML文件的位置類型處理器等等,如下簡單的配置:

          mybatis.type-handlers-package=com.demo.typehandler
          mybatis.configuration.map-underscore-to-camel-case=true

          如果需要通過包掃描的方式注入Mapper,則需要在配置類上加入一個注解:@MapperScan,其中的value屬性指定需要掃描的包。

          直接在全局配置文件配置各種屬性是一種比較簡單的方式,其實的任何組件的整合都有不少于兩種的配置方式,下面來介紹下配置類如何配置。

          MybatisAutoConfiguration自動配置類有如下一斷代碼:

          ??@Bean
          ??@ConditionalOnMissingBean
          ??public?SqlSessionFactory?sqlSessionFactory(DataSource?dataSource)?throws?Exception?{}

          @ConditionalOnMissingBean@Bean真是老搭檔了,意味著我們又可以覆蓋,只需要在IOC容器中注入SqlSessionFactory(Mybatis六劍客之一生產(chǎn)者)

          在自定義配置類中注入即可,如下:

          ?/**
          ?????*?注入SqlSessionFactory
          ?????*/

          ????@Bean("sqlSessionFactory1")
          ????public?SqlSessionFactory?sqlSessionFactory(DataSource?dataSource)?throws?Exception?{
          ????????SqlSessionFactoryBean?sqlSessionFactoryBean?=?new?SqlSessionFactoryBean();
          ????????sqlSessionFactoryBean.setDataSource(dataSource);
          ????????sqlSessionFactoryBean.setMapperLocations(new?PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/**/*.xml"));
          ????????org.apache.ibatis.session.Configuration?configuration?=?new?org.apache.ibatis.session.Configuration();
          ????????//?自動將數(shù)據(jù)庫中的下劃線轉(zhuǎn)換為駝峰格式
          ????????configuration.setMapUnderscoreToCamelCase(true);
          ????????configuration.setDefaultFetchSize(100);
          ????????configuration.setDefaultStatementTimeout(30);
          ????????sqlSessionFactoryBean.setConfiguration(configuration);
          ????????return?sqlSessionFactoryBean.getObject();
          ????}

          以上介紹了配置Mybatis的兩種方式,其實在大多數(shù)場景中使用第一種已經(jīng)夠用了,至于為什么介紹第二種呢?當然是為了多數(shù)據(jù)源的整合而做準備了。

          MybatisAutoConfiguration中有一行很重要的代碼,如下:

          @ConditionalOnSingleCandidate(DataSource.class)

          @ConditionalOnSingleCandidate這個注解的意思是當IOC容器中只有一個候選Bean的實例才會生效。

          這行代碼標注在Mybatis的自動配置類中有何含義呢?下面介紹,哈哈哈~

          多數(shù)據(jù)源如何整合?

          上文留下的問題:為什么的Mybatis自動配置上標注如下一行代碼:

          @ConditionalOnSingleCandidate(DataSource.class)

          以上這行代碼的言外之意:當IOC容器中只有一個數(shù)據(jù)源DataSource,這個自動配置類才會生效。

          哦?照這樣搞,多數(shù)據(jù)源是不能用Mybatis嗎?

          可能大家會有一個誤解,認為多數(shù)據(jù)源就是多個的DataSource并存的,當然這樣說也不是不正確。

          多數(shù)據(jù)源的情況下并不是多個數(shù)據(jù)源并存的,Spring提供了AbstractRoutingDataSource這樣一個抽象類,使得能夠在多數(shù)據(jù)源的情況下任意切換,相當于一個動態(tài)路由的作用,作者稱之為動態(tài)數(shù)據(jù)源。因此Mybatis只需要配置這個動態(tài)數(shù)據(jù)源即可。

          什么是動態(tài)數(shù)據(jù)源?

          動態(tài)數(shù)據(jù)源簡單的說就是能夠自由切換的數(shù)據(jù)源,類似于一個動態(tài)路由的感覺,Spring 提供了一個抽象類AbstractRoutingDataSource,這個抽象類中喲一個屬性,如下:

          private?Map?targetDataSources;

          targetDataSources是一個Map結(jié)構(gòu),所有需要切換的數(shù)據(jù)源都存放在其中,根據(jù)指定的KEY進行切換。當然還有一個默認的數(shù)據(jù)源。

          AbstractRoutingDataSource這個抽象類中有一個抽象方法需要子類實現(xiàn),如下:

          protected?abstract?Object?determineCurrentLookupKey();

          determineCurrentLookupKey()這個方法的返回值決定了需要切換的數(shù)據(jù)源的KEY,就是根據(jù)這個KEYtargetDataSources取值(數(shù)據(jù)源)。

          數(shù)據(jù)源切換如何保證線程隔離?

          數(shù)據(jù)源屬于一個公共的資源,在多線程的情況下如何保證線程隔離呢?不能我這邊切換了影響其他線程的執(zhí)行。

          說到線程隔離,自然會想到ThreadLocal了,將切換數(shù)據(jù)源的KEY(用于從targetDataSources中取值)存儲在ThreadLocal中,執(zhí)行結(jié)束之后清除即可。

          單獨封裝了一個DataSourceHolder,內(nèi)部使用ThreadLocal隔離線程,代碼如下:

          /**
          ?*?使用ThreadLocal存儲切換數(shù)據(jù)源后的KEY
          ?*/

          public?class?DataSourceHolder?{

          ????//線程??本地環(huán)境
          ????private?static?final?ThreadLocal?dataSources?=?new?InheritableThreadLocal();

          ????//設(shè)置數(shù)據(jù)源
          ????public?static?void?setDataSource(String?datasource)?{
          ????????dataSources.set(datasource);
          ????}

          ????//獲取數(shù)據(jù)源
          ????public?static?String?getDataSource()?{
          ????????return?dataSources.get();
          ????}

          ????//清除數(shù)據(jù)源
          ????public?static?void?clearDataSource()?{
          ????????dataSources.remove();
          ????}
          }

          如何構(gòu)造一個動態(tài)數(shù)據(jù)源?

          上文說過只需繼承一個抽象類AbstractRoutingDataSource,重寫其中的一個方法determineCurrentLookupKey()即可。代碼如下:

          /**
          ?*?動態(tài)數(shù)據(jù)源,繼承AbstractRoutingDataSource
          ?*/

          public?class?DynamicDataSource?extends?AbstractRoutingDataSource?{

          ????/**
          ?????*?返回需要使用的數(shù)據(jù)源的key,將會按照這個KEY從Map獲取對應的數(shù)據(jù)源(切換)
          ?????*?@return
          ?????*/

          ????@Override
          ????protected?Object?determineCurrentLookupKey()?{
          ????????//從ThreadLocal中取出KEY
          ????????return?DataSourceHolder.getDataSource();
          ????}

          ????/**
          ?????*?構(gòu)造方法填充Map,構(gòu)建多數(shù)據(jù)源
          ?????*/

          ????public?DynamicDataSource(DataSource?defaultTargetDataSource,?Map?targetDataSources)?{
          ????????//默認的數(shù)據(jù)源,可以作為主數(shù)據(jù)源
          ????????super.setDefaultTargetDataSource(defaultTargetDataSource);
          ????????//目標數(shù)據(jù)源
          ????????super.setTargetDataSources(targetDataSources);
          ????????//執(zhí)行afterPropertiesSet方法,完成屬性的設(shè)置
          ????????super.afterPropertiesSet();
          ????}
          }

          上述代碼很簡單,分析如下:

          1. 一個多參的構(gòu)造方法,指定了默認的數(shù)據(jù)源和目標數(shù)據(jù)源。
          2. 重寫determineCurrentLookupKey()方法,返回數(shù)據(jù)源對應的KEY,這里是直接從ThreadLocal中取值,就是上文封裝的DataSourceHolder

          定義一個注解

          為了操作方便且低耦合,不能每次需要切換的數(shù)據(jù)源的時候都要手動調(diào)一下接口吧,可以定義一個切換數(shù)據(jù)源的注解,如下:

          /**
          ?*?切換數(shù)據(jù)源的注解
          ?*/

          @Target(value?=?ElementType.METHOD)
          @Retention(value?=?RetentionPolicy.RUNTIME)
          @Documented
          public?@interface?SwitchSource?{

          ????/**
          ?????*?默認切換的數(shù)據(jù)源KEY
          ?????*/

          ????String?DEFAULT_NAME?=?"hisDataSource";

          ????/**
          ?????*?需要切換到數(shù)據(jù)的KEY
          ?????*/

          ????String?value()?default?DEFAULT_NAME;
          }

          注解中只有一個value屬性,指定了需要切換數(shù)據(jù)源的KEY

          有注解還不行,當然還要有切面,代碼如下:

          @Aspect
          //優(yōu)先級要設(shè)置在事務(wù)切面執(zhí)行之前
          @Order(1)
          @Component
          @Slf4j
          public?class?DataSourceAspect?{


          ????@Pointcut("@annotation(SwitchSource)")
          ????public?void?pointcut()?{
          ????}

          ????/**
          ?????*?在方法執(zhí)行之前切換到指定的數(shù)據(jù)源
          ?????*?@param?joinPoint
          ?????*/

          ????@Before(value?=?"pointcut()")
          ????public?void?beforeOpt(JoinPoint?joinPoint)?{
          ????????/*因為是對注解進行切面,所以這邊無需做過多判定,直接獲取注解的值,進行環(huán)繞,將數(shù)據(jù)源設(shè)置成遠方,然后結(jié)束后,清楚當前線程數(shù)據(jù)源*/
          ????????Method?method?=?((MethodSignature)?joinPoint.getSignature()).getMethod();
          ????????SwitchSource?switchSource?=?method.getAnnotation(SwitchSource.class);
          ????????log.info("[Switch?DataSource]:"?+?switchSource.value());
          ????????DataSourceHolder.setDataSource(switchSource.value());
          ????}

          ????/**
          ?????*?方法執(zhí)行之后清除掉ThreadLocal中存儲的KEY,這樣動態(tài)數(shù)據(jù)源會使用默認的數(shù)據(jù)源
          ?????*/

          ????@After(value?=?"pointcut()")
          ????public?void?afterOpt()?{
          ????????DataSourceHolder.clearDataSource();
          ????????log.info("[Switch?Default?DataSource]");
          ????}
          }

          這個ASPECT很容易理解,beforeOpt()在方法之前執(zhí)行,取值@SwitchSource中value屬性設(shè)置到ThreadLocal中;afterOpt()方法在方法執(zhí)行之后執(zhí)行,清除掉ThreadLocal中的KEY,保證了如果不切換數(shù)據(jù)源,則用默認的數(shù)據(jù)源。

          如何與Mybatis整合?

          單一數(shù)據(jù)源與Mybatis整合上文已經(jīng)詳細講解了,數(shù)據(jù)源DataSource作為參數(shù)構(gòu)建了SqlSessionFactory,同樣的思想,只需要把這個數(shù)據(jù)源換成動態(tài)數(shù)據(jù)源即可。注入的代碼如下:

          /**
          ?????*?創(chuàng)建動態(tài)數(shù)據(jù)源的SqlSessionFactory,傳入的是動態(tài)數(shù)據(jù)源
          ?????*?@Primary這個注解很重要,如果項目中存在多個SqlSessionFactory,這個注解一定要加上
          ?????*/

          ????@Primary
          ????@Bean("sqlSessionFactory2")
          ????public?SqlSessionFactory?sqlSessionFactoryBean(DynamicDataSource?dynamicDataSource)?throws?Exception?{
          ????????SqlSessionFactoryBean?sqlSessionFactoryBean?=?new?SqlSessionFactoryBean();
          ????????sqlSessionFactoryBean.setDataSource(dynamicDataSource);
          ????????sqlSessionFactoryBean.setMapperLocations(new?PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/**/*.xml"));
          ????????org.apache.ibatis.session.Configuration?configuration?=?new?org.apache.ibatis.session.Configuration();
          ????????configuration.setMapUnderscoreToCamelCase(true);
          ????????configuration.setDefaultFetchSize(100);
          ????????configuration.setDefaultStatementTimeout(30);
          ????????sqlSessionFactoryBean.setConfiguration(configuration);
          ????????return?sqlSessionFactoryBean.getObject();
          ????}

          與Mybatis整合很簡單,只需要把數(shù)據(jù)源替換成自定義的動態(tài)數(shù)據(jù)源DynamicDataSource

          那么動態(tài)數(shù)據(jù)源如何注入到IOC容器中呢?看上文自定義的DynamicDataSource構(gòu)造方法,肯定需要兩個數(shù)據(jù)源了,因此必須先注入兩個或者多個數(shù)據(jù)源到IOC容器中,如下:

          ?/**
          ?????*?@Bean:向IOC容器中注入一個Bean
          ?????*?@ConfigurationProperties:使得配置文件中以spring.datasource為前綴的屬性映射到Bean的屬性中
          ?????*/

          ????@ConfigurationProperties(prefix?=?"spring.datasource")
          ????@Bean("dataSource")
          ????public?DataSource?dataSource(){
          ????????return?new?DruidDataSource();
          ????}

          ????/**
          ?????*?向IOC容器中注入另外一個數(shù)據(jù)源
          ?????*?全局配置文件中前綴是spring.datasource.his
          ?????*/

          ????@Bean(name?=?SwitchSource.DEFAULT_NAME)
          ????@ConfigurationProperties(prefix?=?"spring.datasource.his")
          ????public?DataSource?hisDataSource()?{
          ????????return?DataSourceBuilder.create().build();
          ????}

          以上構(gòu)建的兩個數(shù)據(jù)源,一個是默認的數(shù)據(jù)源,一個是需要切換到的數(shù)據(jù)源(targetDataSources,這樣就組成了動態(tài)數(shù)據(jù)源了。數(shù)據(jù)源的一些信息,比如urlusername需要自己在全局配置文件中根據(jù)指定的前綴配置即可,代碼不再貼出。

          動態(tài)數(shù)據(jù)源的注入代碼如下:

          /**
          ?????*?創(chuàng)建動態(tài)數(shù)據(jù)源的SqlSessionFactory,傳入的是動態(tài)數(shù)據(jù)源
          ?????*?@Primary這個注解很重要,如果項目中存在多個SqlSessionFactory,這個注解一定要加上
          ?????*/

          ????@Primary
          ????@Bean("sqlSessionFactory2")
          ????public?SqlSessionFactory?sqlSessionFactoryBean(DynamicDataSource?dynamicDataSource)?throws?Exception?{
          ????????SqlSessionFactoryBean?sqlSessionFactoryBean?=?new?SqlSessionFactoryBean();
          ????????sqlSessionFactoryBean.setDataSource(dynamicDataSource);
          ????????org.apache.ibatis.session.Configuration?configuration?=?new?org.apache.ibatis.session.Configuration();
          ????????configuration.setMapUnderscoreToCamelCase(true);
          ????????configuration.setDefaultFetchSize(100);
          ????????configuration.setDefaultStatementTimeout(30);
          ????????sqlSessionFactoryBean.setConfiguration(configuration);
          ????????return?sqlSessionFactoryBean.getObject();
          ????}

          這里還有一個問題:IOC中存在多個數(shù)據(jù)源了,那么事務(wù)管理器怎么辦呢?它也懵逼了,到底選擇哪個數(shù)據(jù)源呢?因此事務(wù)管理器肯定還是要重新配置的。

          事務(wù)管理器此時管理的數(shù)據(jù)源將是動態(tài)數(shù)據(jù)源DynamicDataSource,配置如下:

          ???/**
          ?????*?重寫事務(wù)管理器,管理動態(tài)數(shù)據(jù)源
          ?????*/

          ????@Primary
          ????@Bean(value?=?"transactionManager2")
          ????public?PlatformTransactionManager?annotationDrivenTransactionManager(DynamicDataSource?dataSource)?{
          ????????return?new?DataSourceTransactionManager(dataSource);
          ????}

          至此,Mybatis與多數(shù)據(jù)源的整合就完成了。

          演示

          使用也是很簡單,在需要切換數(shù)據(jù)源的方法上方標注@SwitchSource切換到指定的數(shù)據(jù)源即可,如下:

          ????//不開啟事務(wù)
          ????@Transactional(propagation?=?Propagation.NOT_SUPPORTED)
          ????//切換到HIS的數(shù)據(jù)源
          ????@SwitchSource
          ????@Override
          ????public?List?list()?{
          ????????return?hisDeptInfoMapper.listDept();
          ????}

          這樣只要執(zhí)行到這方法將會切換到HIS的數(shù)據(jù)源,方法執(zhí)行結(jié)束之后將會清除,執(zhí)行默認的數(shù)據(jù)源。

          總結(jié)

          本篇文章講了Spring Boot與單數(shù)據(jù)源、Mybatis、多數(shù)據(jù)源之間的整合,希望這篇文章能夠幫助讀者理解多數(shù)據(jù)源的整合,雖說用的不多,但是在有些領(lǐng)域仍然是比較重要的。


          往期推薦

          Spring Boot 中實現(xiàn)跨域的 5 種方式,你一定要知道!


          下個十年高性能 JSON 庫來了:fastjson2!


          ThreadLocal奪命11連問


          瀏覽 24
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  操自拍 | 插40岁女人视频 | 国产欧美日本懂色云播 | 国产在线视频福利播放 | 不卡在线中文字幕 |