Spring Boot 與 多數(shù)據(jù)源那點(diǎn)事兒~

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

最常見的單一應(yīng)用中最多涉及到一個(gè)數(shù)據(jù)庫(kù)雌桑,即是一個(gè)數(shù)據(jù)源(Datasource)汇跨。那么顧名思義务荆,多數(shù)據(jù)源就是在一個(gè)單一應(yīng)用中涉及到了兩個(gè)及以上的數(shù)據(jù)庫(kù)了。

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

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

url函匕、username、password這三個(gè)屬性已經(jīng)唯一確定了一個(gè)數(shù)據(jù)庫(kù)了蚪黑,DataSource則是依賴這三個(gè)創(chuàng)建出來(lái)的盅惜。則多數(shù)據(jù)源即是配置多個(gè)DataSource(暫且這么理解)。

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

正如前言介紹到的一個(gè)場(chǎng)景抒寂,相信大多數(shù)做過(guò)醫(yī)療系統(tǒng)的都會(huì)和HIS打交道,為了簡(jiǎn)化護(hù)士以及醫(yī)生的操作流程掠剑,必須要將必要的信息從HIS系統(tǒng)對(duì)接過(guò)來(lái)屈芜,據(jù)我了解的大致有兩種方案如下:

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

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

當(dāng)然多數(shù)據(jù)源的使用場(chǎng)景還是有很多的迹辐,以上只是簡(jiǎn)單的一個(gè)場(chǎng)景。

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

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

<!--druid連接池-->
<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>druid-spring-boot-starter</artifactId>
   <version>1.1.9</version>
</dependency>

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

Druid連接池的starter的自動(dòng)配置類是DruidDataSourceAutoConfigure详羡,類上標(biāo)注如下一行注解:

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

@EnableConfigurationProperties這個(gè)注解使得配置文件中的配置生效并且映射到指定類的屬性仍律。
DruidStatProperties中指定的前綴是spring.datasource.druid,這個(gè)配置主要是用來(lái)設(shè)置連接池的一些參數(shù)实柠。

DataSourceProperties中指定的前綴是spring.datasource水泉,這個(gè)主要是用來(lái)設(shè)置數(shù)據(jù)庫(kù)的url、username窒盐、password等信息草则。

因此我們只需要在全局配置文件中指定數(shù)據(jù)庫(kù)的一些配置以及連接池的一些配置信息即可,前綴分別是spring.datasource.druid蟹漓、spring.datasource炕横,以下是個(gè)人隨便配置的(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
#獲取連接最大等待時(shí)間
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
#配置間隔多久才進(jìn)行一次檢測(cè),檢測(cè)需要關(guān)閉的空閑連接牧牢,單位是毫秒
spring.datasource.druid.time-between-eviction-runs-millis=60000
#置一個(gè)連接在池中最小生存的時(shí)間看锉,單位是毫秒
spring.datasource.druid.min-evictable-idle-time-millis=25200000
#spring.datasource.druid.max-evictable-idle-time-millis=
#打開removeAbandoned功能,多少時(shí)間內(nèi)必須關(guān)閉連接
spring.datasource.druid.removeAbandoned=true
#1800秒,也就是30分鐘
spring.datasource.druid.remove-abandoned-timeout=1800
#<!-- 1800秒塔鳍,也就是30分鐘 -->
spring.datasource.druid.log-abandoned=true
spring.datasource.druid.filters=mergeStat

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

在自動(dòng)配置類中DruidDataSourceAutoConfigure中有如下一段代碼:

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

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

因此我們?cè)谧远x的配置類中定義如下配置即可:

/**
     * @Bean:向IOC容器中注入一個(gè)Bean
     * @ConfigurationProperties:使得配置文件中以spring.datasource為前綴的屬性映射到Bean的屬性中
     * @return
     */
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource dataSource(){
        //做一些其他的自定義配置放前,比如密碼加密等......
        return new DruidDataSource();
    }

以上介紹了兩種數(shù)據(jù)源的配置方式忿磅,第一種比較簡(jiǎn)單,第二種適合擴(kuò)展凭语,按需選擇葱她。

整合Mybatis

Spring Boot 整合Mybatis其實(shí)很簡(jiǎn)單偷线,簡(jiǎn)單的幾步就搞定链瓦,首先添加依賴:

<dependency>
     <groupId>org.mybatis.spring.boot</groupId>
     <artifactId>mybatis-spring-boot-starter</artifactId>
     <version>2.0.0</version>
</dependency>

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

@EnableConfigurationProperties(MybatisProperties.class)

老套路了刁愿,全局配置文件中配置前綴為mybatis的配置將會(huì)映射到該類中的屬性炒辉。

可配置的東西很多豪墅,比如XML文件的位置、類型處理器等等黔寇,如下簡(jiǎn)單的配置:

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

如果需要通過(guò)包掃描的方式注入Mapper偶器,則需要在配置類上加入一個(gè)注解:@MapperScan,其中的value屬性指定需要掃描的包缝裤。

直接在全局配置文件配置各種屬性是一種比較簡(jiǎn)單的方式屏轰,其實(shí)的任何組件的整合都有不少于兩種的配置方式,下面來(lái)介紹下配置類如何配置憋飞。

MybatisAutoConfiguration自動(dòng)配置類有如下一斷代碼:

  @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();
        // 自動(dòng)將數(shù)據(jù)庫(kù)中的下劃線轉(zhuǎn)換為駝峰格式
        configuration.setMapUnderscoreToCamelCase(true);
        configuration.setDefaultFetchSize(100);
        configuration.setDefaultStatementTimeout(30);
        sqlSessionFactoryBean.setConfiguration(configuration);
        return sqlSessionFactoryBean.getObject();
    }

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

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

@ConditionalOnSingleCandidate(DataSource.class)

@ConditionalOnSingleCandidate這個(gè)注解的意思是當(dāng)IOC容器中只有一個(gè)候選Bean的實(shí)例才會(huì)生效驴党。

這行代碼標(biāo)注在Mybatis的自動(dòng)配置類中有何含義呢?下面介紹获茬,哈哈哈~

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

上文留下的問(wèn)題:為什么的Mybatis自動(dòng)配置上標(biāo)注如下一行代碼:

@ConditionalOnSingleCandidate(DataSource.class)

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

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

可能大家會(huì)有一個(gè)誤解把还,認(rèn)為多數(shù)據(jù)源就是多個(gè)的DataSource并存的,當(dāng)然這樣說(shuō)也不是不正確。

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

什么是動(dòng)態(tài)數(shù)據(jù)源缀踪?

動(dòng)態(tài)數(shù)據(jù)源簡(jiǎn)單的說(shuō)就是能夠自由切換的數(shù)據(jù)源腺晾,類似于一個(gè)動(dòng)態(tài)路由的感覺,Spring 提供了一個(gè)抽象類AbstractRoutingDataSource辜贵,這個(gè)抽象類中喲一個(gè)屬性,如下:

private Map<Object, Object> targetDataSources;

targetDataSources是一個(gè)Map結(jié)構(gòu)归形,所有需要切換的數(shù)據(jù)源都存放在其中托慨,根據(jù)指定的KEY進(jìn)行切換。當(dāng)然還有一個(gè)默認(rèn)的數(shù)據(jù)源暇榴。

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

protected abstract Object determineCurrentLookupKey();

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

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

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

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

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

/**
 * 使用ThreadLocal存儲(chǔ)切換數(shù)據(jù)源后的KEY
 */
public class DataSourceHolder {

    //線程  本地環(huán)境
    private static final ThreadLocal<String> 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)造一個(gè)動(dòng)態(tài)數(shù)據(jù)源?

上文說(shuō)過(guò)只需繼承一個(gè)抽象類AbstractRoutingDataSource评也,重寫其中的一個(gè)方法determineCurrentLookupKey()即可炼杖。代碼如下:

/**
 * 動(dòng)態(tài)數(shù)據(jù)源,繼承AbstractRoutingDataSource
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    /**
     * 返回需要使用的數(shù)據(jù)源的key盗迟,將會(huì)按照這個(gè)KEY從Map獲取對(duì)應(yīng)的數(shù)據(jù)源(切換)
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        //從ThreadLocal中取出KEY
        return DataSourceHolder.getDataSource();
    }

    /**
     * 構(gòu)造方法填充Map坤邪,構(gòu)建多數(shù)據(jù)源
     */
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        //默認(rèn)的數(shù)據(jù)源,可以作為主數(shù)據(jù)源
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        //目標(biāo)數(shù)據(jù)源
        super.setTargetDataSources(targetDataSources);
        //執(zhí)行afterPropertiesSet方法罚缕,完成屬性的設(shè)置
        super.afterPropertiesSet();
    }
}

上述代碼很簡(jiǎn)單罩扇,分析如下:

1.一個(gè)多參的構(gòu)造方法,指定了默認(rèn)的數(shù)據(jù)源和目標(biāo)數(shù)據(jù)源。
2.重寫determineCurrentLookupKey()方法喂饥,返回?cái)?shù)據(jù)源對(duì)應(yīng)的KEY消约,這里是直接從ThreadLocal中取值,就是上文封裝的DataSourceHolder员帮。

定義一個(gè)注解

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

/**
 * 切換數(shù)據(jù)源的注解
 */
@Target(value = ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
public @interface SwitchSource {

    /**
     * 默認(rèn)切換的數(shù)據(jù)源KEY
     */
    String DEFAULT_NAME = "hisDataSource";

    /**
     * 需要切換到數(shù)據(jù)的KEY
     */
    String value() default DEFAULT_NAME;
}

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

有注解還不行硝岗,當(dāng)然還要有切面氢哮,代碼如下:

@Aspect
//優(yōu)先級(jí)設(shè)置到最高
@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
@Slf4j
public class DataSourceAspect {


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

    /**
     * 在方法執(zhí)行之前切換到指定的數(shù)據(jù)源
     * @param joinPoint
     */
    @Before(value = "pointcut()")
    public void beforeOpt(JoinPoint joinPoint) {
        /*因?yàn)槭菍?duì)注解進(jìn)行切面,所以這邊無(wú)需做過(guò)多判定型檀,直接獲取注解的值冗尤,進(jìn)行環(huán)繞,將數(shù)據(jù)源設(shè)置成遠(yuǎn)方胀溺,然后結(jié)束后裂七,清楚當(dāng)前線程數(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中存儲(chǔ)的KEY,這樣動(dòng)態(tài)數(shù)據(jù)源會(huì)使用默認(rèn)的數(shù)據(jù)源
     */
    @After(value = "pointcut()")
    public void afterOpt() {
        DataSourceHolder.clearDataSource();
        log.info("[Switch Default DataSource]");
    }
}

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

如何與Mybatis整合嫉称?

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

/**
     * 創(chuàng)建動(dòng)態(tài)數(shù)據(jù)源的SqlSessionFactory,傳入的是動(dòng)態(tài)數(shù)據(jù)源
     * @Primary這個(gè)注解很重要蒲稳,如果項(xiàng)目中存在多個(gè)SqlSessionFactory氮趋,這個(gè)注解一定要加上
     */
    @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();
    }

與Mybatis整合很簡(jiǎn)單,只需要把數(shù)據(jù)源替換成自定義的動(dòng)態(tài)數(shù)據(jù)源DynamicDataSource江耀。

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

 /**
     * @Bean:向IOC容器中注入一個(gè)Bean
     * @ConfigurationProperties:使得配置文件中以spring.datasource為前綴的屬性映射到Bean的屬性中
     */
    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean("dataSource")
    public DataSource dataSource(){
        return new DruidDataSource();
    }

    /**
     * 向IOC容器中注入另外一個(gè)數(shù)據(jù)源
     * 全局配置文件中前綴是spring.datasource.his
     */
    @Bean(name = SwitchSource.DEFAULT_NAME)
    @ConfigurationProperties(prefix = "spring.datasource.his")
    public DataSource hisDataSource() {
        return DataSourceBuilder.create().build();
    }

以上構(gòu)建的兩個(gè)數(shù)據(jù)源晾腔,一個(gè)是默認(rèn)的數(shù)據(jù)源,一個(gè)是需要切換到的數(shù)據(jù)源(targetDataSources)啊犬,這樣就組成了動(dòng)態(tài)數(shù)據(jù)源了灼擂。數(shù)據(jù)源的一些信息,比如url觉至,username需要自己在全局配置文件中根據(jù)指定的前綴配置即可剔应,代碼不再貼出。

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

/**
     * 創(chuàng)建動(dòng)態(tài)數(shù)據(jù)源的SqlSessionFactory语御,傳入的是動(dòng)態(tài)數(shù)據(jù)源
     * @Primary這個(gè)注解很重要峻贮,如果項(xiàng)目中存在多個(gè)SqlSessionFactory,這個(gè)注解一定要加上
     */
    @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();
    }

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

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

   /**
     * 重寫事務(wù)管理器惜辑,管理動(dòng)態(tài)數(shù)據(jù)源
     */
    @Primary
    @Bean(value = "transactionManager2")
    public PlatformTransactionManager annotationDrivenTransactionManager(DynamicDataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

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

演示
使用也是很簡(jiǎn)單盛撑,在需要切換數(shù)據(jù)源的方法上方標(biāo)注@SwitchSource切換到指定的數(shù)據(jù)源即可,如下:

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

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

最后

感謝大家看到這里胎撇,如果本文有什么不足之處介粘,歡迎多多指教;如果你覺得對(duì)你有幫助晚树,請(qǐng)給我點(diǎn)個(gè)贊姻采。

也歡迎大家關(guān)注我的公眾號(hào):程序員麥冬,每天更新行業(yè)資訊爵憎!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末慨亲,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子宝鼓,更是在濱河造成了極大的恐慌刑棵,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件愚铡,死亡現(xiàn)場(chǎng)離奇詭異蛉签,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門碍舍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)柠座,“玉大人,你說(shuō)我怎么就攤上這事乒验∮匏恚” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵锻全,是天一觀的道長(zhǎng)狂塘。 經(jīng)常有香客問(wèn)我,道長(zhǎng)鳄厌,這世上最難降的妖魔是什么荞胡? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮了嚎,結(jié)果婚禮上泪漂,老公的妹妹穿的比我還像新娘。我一直安慰自己歪泳,他們只是感情好萝勤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著呐伞,像睡著了一般敌卓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上伶氢,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天趟径,我揣著相機(jī)與錄音,去河邊找鬼癣防。 笑死蜗巧,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蕾盯。 我是一名探鬼主播幕屹,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼级遭!你這毒婦竟也來(lái)了香嗓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤装畅,失蹤者是張志新(化名)和其女友劉穎靠娱,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體掠兄,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡像云,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年锌雀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片迅诬。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡腋逆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出侈贷,到底是詐尸還是另有隱情惩歉,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布俏蛮,位于F島的核電站撑蚌,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏搏屑。R本人自食惡果不足惜争涌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望辣恋。 院中可真熱鬧亮垫,春花似錦、人聲如沸伟骨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)携狭。三九已至继蜡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間暑中,已是汗流浹背壹瘟。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工鲫剿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鳄逾,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓灵莲,卻偏偏與公主長(zhǎng)得像雕凹,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子政冻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354