一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - Spring+Mybatis動態(tài)切換數(shù)據(jù)源的方法

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

2021-03-27 14:09pengyuzhu Java教程

這篇文章主要為大家詳細(xì)介紹了Spring+Mybatis動態(tài)切換數(shù)據(jù)源的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下

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

1、運營平臺有自身的數(shù)據(jù)庫,維護(hù)用戶、角色、菜單、部分以及權(quán)限等基本功能。

2、運營平臺還需要提供其他不同服務(wù)(服務(wù)a,服務(wù)b)的后臺運營,服務(wù)a、服務(wù)b的數(shù)據(jù)庫是獨立的。

所以,運營平臺至少要連三個庫:運營庫,a庫,b庫,并且希望達(dá)到針對每個功能請求能夠自動切換到對應(yīng)的數(shù)據(jù)源(我最終實現(xiàn)是針對service的方法級別進(jìn)行切換的,也可以實現(xiàn)針對每個dao層的方法進(jìn)行切換。我們系統(tǒng)的功能是相互之間比較獨立的)。

第一步:配置多數(shù)據(jù)源

1、定義數(shù)據(jù)源:

我采用的數(shù)據(jù)源是阿里的druiddatasource(用dbcp也行,這個隨便)。配置如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<!-- 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>

我配置了三個數(shù)據(jù)源:opdatasource(運營平臺本身的數(shù)據(jù)源),serveradatasource,serverbdatasource。

2、配置multipledatasource

multipledatasource相當(dāng)于以上三個數(shù)據(jù)源的一個代理,真正與spring/mybatis相結(jié)合的時multipledatasource,和單獨配置的datasource使用沒有分別:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<!-- 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)實現(xiàn) -->
  <bean id="mapperscannerconfigurer" class="org.mybatis.spring.mapper.mapperscannerconfigurer">
    <!-- 對dao 接口動態(tài)實現(xiàn),需要知道接口在哪 -->
    <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>
 
 
  <!-- 事務(wù)管理配置multipledatasource -->
  <bean id="transactionmanager"
    class="org.springframework.jdbc.datasource.datasourcetransactionmanager">
    <property name="datasource" ref="multipledatasource"></property>
  </bean>

了解了multipledatasource所處的位置之后,接下來重點看下multipledatasource怎么實現(xiàn),配置文件如下:

?
1
2
3
4
5
6
7
8
9
10
<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>

實現(xiàn)的java代碼如下,不需要過多的解釋,很一目了然:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import org.springframework.jdbc.datasource.lookup.abstractroutingdatasource;
 
/**
 *
 * @classname: multipledatasource
 * @description: 配置多個數(shù)據(jù)源<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,實現(xiàn)抽象方法determinecurrentlookupkey,這個方法會在每次獲得數(shù)據(jù)庫連接connection的時候之前,決定本次連接的數(shù)據(jù)源datasource,可以看下spring的代碼就很清晰了:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*獲取連接*/
  public connection getconnection()
    throws sqlexception {
    return determinetargetdatasource().getconnection();
  }
 
  protected datasource determinetargetdatasource() {
    assert.notnull(this.resolveddatasources, "datasource router not initialized");
    /*此處的determinecurrentlookupkey為抽象接口,獲取具體的數(shù)據(jù)源名稱*/
    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實現(xiàn)的接口*/
  protected abstract object determinecurrentlookupkey();

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

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

我們來看動態(tài)切換數(shù)據(jù)源的aop實現(xiàn):

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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;
 
/**
 * 數(shù)據(jù)源切換aop
 *
 * @author yuzhu.peng
 * @since 2018-01-15
 */
@aspect
@order(1)
public class multipledatasourceinterceptor {
  /**
   * 攔截器對所有的業(yè)務(wù)實現(xiàn)類請求之前進(jìn)行數(shù)據(jù)源切換 特別注意,由于用到了多數(shù)據(jù)源,mapper的調(diào)用最好只在*serviceimpl,不然調(diào)用到非默認(rèn)數(shù)據(jù)源的表時,會報表不存在的異常
   *
   * @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的設(shè)置為servera數(shù)據(jù)源,否則默認(rèn)為后臺的數(shù)據(jù)源
    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);
    }
  }
  
  /**
   * 當(dāng)操作完成時,釋放當(dāng)前的數(shù)據(jù)源 如果不釋放,頻繁點擊時會發(fā)生數(shù)據(jù)源沖突,本是另一個數(shù)據(jù)源的表,結(jié)果跑到另外一個數(shù)據(jù)源去,報表不存在
   *
   * @param joinpoint
   * @throws throwable
   */
  @after("execution(* com.xxxx.service..*.*serviceimpl.*(..))")
  public void removedatasoruce(joinpoint joinpoint)
    throws throwable {
    multipledatasource.removedatasourcekey();
  }
}

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

其他:

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

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

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

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

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。

原文鏈接:https://www.cnblogs.com/zackzhuzi/archive/2018/01/26/8359940.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲欧美自偷自拍另类小说 | 国产精品亚洲午夜一区二区三区 | 91一区二区在线观看精品 | 好吊色青青青国产综合在线观看 | 亚洲第一页综合 | 福利久草 | 国产香蕉国产精品偷在线观看 | 欧美日韩在线一区 | 四虎在线最新永久免费 | 天堂成人在线视频 | 火影忍者小南裸羞羞漫画 | 久久免费资源福利资源站 | 无码欧美喷潮福利XXXX | 欧美性色老妇人 | 青青青在线免费 | 亚洲四虎 | 女教师巨大乳孔中文字幕免费 | 嫩模被黑人粗大挺进 | 草草草视频在线观看 | 动漫jk美女被爆羞羞漫画 | 免费在线观看小视频 | 日本指交 | 四虎精品在线视频 | 四虎成人免费观看在线网址 | 无敌秦墨漫画免费阅读 | 国外欧美一区另类中文字幕 | 日本在线视频免费观看 | 蜜桃视频在线观看官网 | 欧美伦理影院 | 4虎影视国产在线观看精品 4s4s4s4s色大众影视 | tube62hdxxxx日本| 午夜伦伦电影理论片大片 | 亚洲精品AV无码喷奶水糖心 | 91av爱爱 | 国产精品亚欧美一区二区三区 | 精品国产自在在线在线观看 | 欧美一区二区免费 | 亚洲丰满女人ass硕大 | 好大好爽好硬我要喷水了 | 特黄aa级毛片免费视频播放 | 国产一级毛片国语版 |