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

Spring+Mybatis動態(tài)切換數據源的方法

 更新時間:2018年01月27日 14:04:50   作者:pengyuzhu  
這篇文章主要為大家詳細介紹了Spring+Mybatis動態(tài)切換數據源的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下

功能需求是公司要做一個大的運營平臺:

1、運營平臺有自身的數據庫,維護用戶、角色、菜單、部分以及權限等基本功能。

2、運營平臺還需要提供其他不同服務(服務A,服務B)的后臺運營,服務A、服務B的數據庫是獨立的。

所以,運營平臺至少要連三個庫:運營庫,A庫,B庫,并且希望達到針對每個功能請求能夠自動切換到對應的數據源(我最終實現是針對Service的方法級別進行切換的,也可以實現針對每個DAO層的方法進行切換。我們系統(tǒng)的功能是相互之間比較獨立的)。

第一步:配置多數據源

1、定義數據源:

我采用的數據源是阿里的DruidDataSource(用DBCP也行,這個隨便)。配置如下:

<!-- op dataSource -->
  <bean id="opDataSource" class="com.alibaba.druid.pool.DruidDataSource"
    init-method="init" destroy-method="close">
    <property name="url" value="${db.master.url}" />
    <property name="username" value="${db.master.user}" />
    <property name="password" value="${db.master.password}" />
    <property name="driverClassName" value="${db.master.driver}" />
    <property name="initialSize" value="5" />
    <property name="maxActive" value="100" />
    <property name="minIdle" value="10" />
    <property name="maxWait" value="60000" />
    <property name="validationQuery" value="SELECT 'x'" />
    <property name="testOnBorrow" value="false" />
    <property name="testOnReturn" value="false" />
    <property name="testWhileIdle" value="true" />
    <property name="timeBetweenEvictionRunsMillis" value="600000" />
    <property name="minEvictableIdleTimeMillis" value="300000" />
    <property name="removeAbandoned" value="true" />
    <property name="removeAbandonedTimeout" value="1800" />
    <property name="logAbandoned" value="true" />
    <!-- 配置監(jiān)控統(tǒng)計攔截的filters -->
    <property name="filters" value="config,mergeStat,wall,log4j2" />
    <property name="connectionProperties" value="config.decrypt=true" />
  </bean>

  <!-- serverA dataSource -->
  <bean id="serverADataSource" class="com.alibaba.druid.pool.DruidDataSource"
    init-method="init" destroy-method="close">
    <property name="url" value="${db.serverA.master.url}" />
    <property name="username" value="${db.serverA.master.user}" />
    <property name="password" value="${db.serverA.master.password}" />
    <property name="driverClassName" value="${db.serverA.master.driver}" />
    <property name="initialSize" value="5" />
    <property name="maxActive" value="100" />
    <property name="minIdle" value="10" />
    <property name="maxWait" value="60000" />
    <property name="validationQuery" value="SELECT 'x'" />
    <property name="testOnBorrow" value="false" />
    <property name="testOnReturn" value="false" />
    <property name="testWhileIdle" value="true" />
    <property name="timeBetweenEvictionRunsMillis" value="600000" />
    <property name="minEvictableIdleTimeMillis" value="300000" />
    <property name="removeAbandoned" value="true" />
    <property name="removeAbandonedTimeout" value="1800" />
    <property name="logAbandoned" value="true" />
    <!-- 配置監(jiān)控統(tǒng)計攔截的filters -->
    <property name="filters" value="config,mergeStat,wall,log4j2" />
    <property name="connectionProperties" value="config.decrypt=true" />
  </bean>

  <!-- serverB dataSource -->
  <bean id="serverBDataSource" class="com.alibaba.druid.pool.DruidDataSource"
    init-method="init" destroy-method="close">
    <property name="url" value="${db.serverB.master.url}" />
    <property name="username" value="${db.serverB.master.user}" />
    <property name="password" value="${db.serverB.master.password}" />
    <property name="driverClassName" value="${db.serverB.master.driver}" />
    <property name="initialSize" value="5" />
    <property name="maxActive" value="100" />
    <property name="minIdle" value="10" />
    <property name="maxWait" value="60000" />
    <property name="validationQuery" value="SELECT 'x'" />
    <property name="testOnBorrow" value="false" />
    <property name="testOnReturn" value="false" />
    <property name="testWhileIdle" value="true" />
    <property name="timeBetweenEvictionRunsMillis" value="600000" />
    <property name="minEvictableIdleTimeMillis" value="300000" />
    <property name="removeAbandoned" value="true" />
    <property name="removeAbandonedTimeout" value="1800" />
    <property name="logAbandoned" value="true" />
    <!-- 配置監(jiān)控統(tǒng)計攔截的filters -->
    <property name="filters" value="config,mergeStat,wall,log4j2" />
    <property name="connectionProperties" value="config.decrypt=true" />
  </bean>

我配置了三個數據源:oPDataSource(運營平臺本身的數據源),serverADataSource,serverBDataSource。

2、配置multipleDataSource

multipleDataSource相當于以上三個數據源的一個代理,真正與Spring/Mybatis相結合的時multipleDataSource,和單獨配置的DataSource使用沒有分別:

<!-- Spring整合Mybatis:配置multipleDatasource -->
  <bean id="sqlSessionFactory"
    class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
    <property name="dataSource" ref="multipleDataSource" />
    <!-- 自動掃描Mapping.xml文件 -->
    <property name="mapperLocations">
      <list>
        <value>classpath*:/sqlMapperXml/*.xml</value>
        <value>classpath*:/sqlMapperXml/*/*.xml</value>
      </list>
    </property>
    <property name="configLocation" value="classpath:xml/mybatis-config.xml"></property>
    <property name="typeAliasesPackage" value="com.XXX.platform.model" />
    <property name="globalConfig" ref="globalConfig" />
    <property name="plugins">
      <array>
        <!-- 分頁插件配置 -->
        <bean id="paginationInterceptor"
          class="com.baomidou.mybatisplus.plugins.PaginationInterceptor">
          <property name="dialectType" value="mysql" />
          <property name="optimizeType" value="aliDruid" />
        </bean>
      </array>
    </property>
  </bean>
  
  <!-- MyBatis 動態(tài)實現 -->
  <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!-- 對Dao 接口動態(tài)實現,需要知道接口在哪 -->
    <property name="basePackage" value="com.XXX.platform.mapper" />
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
  </bean>  
  <!-- MP 全局配置 -->
  <bean id="globalConfig" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
    <property name="idType" value="0" />
    <property name="dbColumnUnderline" value="true" />
  </bean>


  <!-- 事務管理配置multipleDataSource -->
  <bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="multipleDataSource"></property>
  </bean>

了解了multipleDataSource所處的位置之后,接下來重點看下multipleDataSource怎么實現,配置文件如下:

<bean id="multipleDataSource" class="com.xxxx.platform.commons.db.MultipleDataSource">
    <property name="defaultTargetDataSource" ref="opDataSource" />
    <property name="targetDataSources">
      <map>
        <entry key="opDataSource" value-ref="opDataSource" />
        <entry key="serverADataSource" value-ref="serverADataSource" />
        <entry key="serverBDataSource" value-ref="serverBDataSource" />
      </map>
    </property>
  </bean>

實現的Java代碼如下,不需要過多的解釋,很一目了然:

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * 
 * @ClassName: MultipleDataSource
 * @Description: 配置多個數據源<br>
 * @author: yuzhu.peng
 * @date: 2018年1月12日 下午4:37:25
 */
public class MultipleDataSource extends AbstractRoutingDataSource {
  
  private static final ThreadLocal<String> dataSourceKey = new InheritableThreadLocal<String>();
  
  public static void setDataSourceKey(String dataSource) {
    dataSourceKey.set(dataSource);
  }
  
  @Override
  protected Object determineCurrentLookupKey() {
    return dataSourceKey.get();
  }
  
  public static void removeDataSourceKey() {
    dataSourceKey.remove();
  }
}

繼承自spring的AbstractRoutingDataSource,實現抽象方法determineCurrentLookupKey,這個方法會在每次獲得數據庫連接Connection的時候之前,決定本次連接的數據源Datasource,可以看下Spring的代碼就很清晰了:

/*獲取連接*/
  public Connection getConnection()
    throws SQLException {
    return determineTargetDataSource().getConnection();
  }

  protected DataSource determineTargetDataSource() {
    Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
    /*此處的determineCurrentLookupKey為抽象接口,獲取具體的數據源名稱*/
    Object lookupKey = determineCurrentLookupKey();
    DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
    if ((dataSource == null) && (((this.lenientFallback) || (lookupKey == null)))) {
      dataSource = this.resolvedDefaultDataSource;
    }
    if (dataSource == null) {
      throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
    }
    return dataSource;
  }
  /*抽象接口:也即我們的multipleDataSource實現的接口*/
  protected abstract Object determineCurrentLookupKey();

第二步:每次請求(Service方法級別)動態(tài)切換數據源

 實現思路是利用Spring的AOP思想,攔截每次的Service方法調用,然后根據方法的整體路徑名,動態(tài)切換multipleDataSource中的數據的key。我們的項目,針對不同服務也即不同數據庫的操作,是彼此之間互相獨立的,不太建議在同一個service方法中調用不同的數據源,這樣的話需要將動態(tài)判斷是否需要切換的頻次(AOP攔截的頻次)放在DAO級別,也就是SQL級別。另外,還不方便進行事務管理。

我們來看動態(tài)切換數據源的AOP實現:

import java.lang.reflect.Proxy;

import org.apache.commons.lang.ClassUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;

/**
 * 數據源切換AOP
 * 
 * @author yuzhu.peng
 * @since 2018-01-15
 */
@Aspect
@Order(1)
public class MultipleDataSourceInterceptor {
  /**
   * 攔截器對所有的業(yè)務實現類請求之前進行數據源切換 特別注意,由于用到了多數據源,Mapper的調用最好只在*ServiceImpl,不然調用到非默認數據源的表時,會報表不存在的異常
   * 
   * @param joinPoint
   * @throws Throwable
   */
  @Before("execution(* com.xxxx.platform.service..*.*ServiceImpl.*(..))")
  public void setDataSoruce(JoinPoint joinPoint)
    throws Throwable {
    Class<?> clazz = joinPoint.getTarget().getClass();
    String className = clazz.getName();
    if (ClassUtils.isAssignable(clazz, Proxy.class)) {
      className = joinPoint.getSignature().getDeclaringTypeName();
    }
    // 對類名含有serverA的設置為serverA數據源,否則默認為后臺的數據源
    if (className.contains(".serverA.")) {
      MultipleDataSource.setDataSourceKey(DBConstant.DATA_SOURCE_serverA);
    }
    else if (className.contains(".serverB.")) {
      MultipleDataSource.setDataSourceKey(DBConstant.DATA_SOURCE_serverB);
    }
    else {
      MultipleDataSource.setDataSourceKey(DBConstant.DATA_SOURCE_OP);
    }
  }
  
  /**
   * 當操作完成時,釋放當前的數據源 如果不釋放,頻繁點擊時會發(fā)生數據源沖突,本是另一個數據源的表,結果跑到另外一個數據源去,報表不存在
   * 
   * @param joinPoint
   * @throws Throwable
   */
  @After("execution(* com.xxxx.service..*.*ServiceImpl.*(..))")
  public void removeDataSoruce(JoinPoint joinPoint)
    throws Throwable {
    MultipleDataSource.removeDataSourceKey();
  }
}

攔截所有的ServiceImpl方法,根據方法的全限定名去判斷屬于那個數據源的功能,然后選擇相應的數據源,發(fā)放執(zhí)行完后,釋放當前的數據源。注意我用到了Spring的 @Order,注解,接下來會講到,當定義多個AOP的時候,order是很有用的。

其他:

一開始項目中并沒有引入事務,所以一切都OK,每次都能訪問到正確的數據源,當加入SPring的事務管理后,不能動態(tài)切換數據源了(也好像是事務沒有生效,反正是二者沒有同時有效),后來發(fā)現原因是AOP的執(zhí)行順序問題,所以用到了上邊提到的SPring的Order:

 order越小,先被執(zhí)行。至此,既可以動態(tài)切換數據源,又可以成功用事務(在同一個數據源)。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • java應用cpu占用過高問題分析及解決方法

    java應用cpu占用過高問題分析及解決方法

    這篇文章主要介紹了java應用cpu占用過高問題分析及解決方法,具有一定參考價值,需要的朋友可以參考下。
    2017-09-09
  • Java網絡編程基礎教程之Socket入門實例

    Java網絡編程基礎教程之Socket入門實例

    這篇文章主要介紹了Java網絡編程基礎教程之Socket入門實例,本文講解了創(chuàng)建Socket、Socket發(fā)送數據、Socket讀取數據、關閉Socket等內容,都是最基礎的知識點,需要的朋友可以參考下
    2014-09-09
  • maven依賴的version聲明控制方式

    maven依賴的version聲明控制方式

    這篇文章主要介紹了maven依賴的version聲明控制方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • 淺談Java中幾種常見的比較器的實現方法

    淺談Java中幾種常見的比較器的實現方法

    下面小編就為大家?guī)硪黄獪\談Java中幾種常見的比較器的實現方法。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • SpringCloud配置刷新原理解析

    SpringCloud配置刷新原理解析

    這篇文章主要介紹了SpringCloud之配置刷新的原理,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-05-05
  • Project?Reactor?響應式范式編程

    Project?Reactor?響應式范式編程

    這篇文章主要為大家介紹了Project?Reactor?響應式范式編程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-04-04
  • mybatisPlus中批量刪除的示例代碼

    mybatisPlus中批量刪除的示例代碼

    本文主要介紹了mybatisPlus中批量刪除的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-06-06
  • Kotlin傳遞可變長參數給Java可變參數實例代碼

    Kotlin傳遞可變長參數給Java可變參數實例代碼

    這篇文章主要介紹了Kotlin傳遞可變長參數給Java可變參數實例代碼,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下
    2018-01-01
  • Java中的多態(tài)用法實例分析

    Java中的多態(tài)用法實例分析

    這篇文章主要介紹了Java中的多態(tài)用法,較為詳細的分析了java中多態(tài)的概念與相關的實現技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-04-04
  • Spring的請求映射handlerMapping以及原理詳解

    Spring的請求映射handlerMapping以及原理詳解

    這篇文章主要介紹了Spring的請求映射handlerMapping以及原理詳解,我們每次發(fā)請求,它到底是怎么找到我們哪個方法來去處理這個請求,因為我們知道所有的請求過來都會來到DispatcherServlet,springboot底層還是使用的是springMVC,需要的朋友可以參考下
    2023-08-08

最新評論

纳雍县| 宁化县| 崇礼县| 洛川县| 鄂尔多斯市| 邻水| 海林市| 南靖县| 宜君县| 洪湖市| 冕宁县| 临夏市| 城步| 邯郸市| 大安市| 榆中县| 淄博市| 武安市| 宜州市| 沂源县| 嘉黎县| 湛江市| 中宁县| 城市| 禹城市| 武乡县| 大冶市| 枣庄市| 玛曲县| 宁强县| 钟祥市| 淄博市| 成都市| 郓城县| 亳州市| 托克逊县| 科技| 昂仁县| 山西省| 昆明市| 临城县|