Spring Boot 集成Mybatis實(shí)現(xiàn)主從(多數(shù)據(jù)源)分離方案

新建一個(gè)Maven項(xiàng)目祭芦,最終項(xiàng)目結(jié)構(gòu)如下:

多數(shù)據(jù)源注入到sqlSessionFactory

POM增加如下依賴:

<!--JSON-->com.fasterxml.jackson.corejackson-corecom.fasterxml.jackson.corejackson-databindcom.fasterxml.jackson.datatypejackson-datatype-jodacom.fasterxml.jackson.modulejackson-module-parameter-namesorg.springframework.bootspring-boot-starter-jdbcmysqlmysql-connector-javacom.alibabadruid1.0.11<!--mybatis-->org.mybatis.spring.bootmybatis-spring-boot-starter1.1.1<!--mapper-->tk.mybatismapper-spring-boot-starter1.1.0<!--pagehelper-->com.github.pagehelperpagehelper-spring-boot-starter1.1.0mybatis-spring-boot-starterorg.mybatis.spring.boot

這里需要注意的是:項(xiàng)目是通過(guò)擴(kuò)展mybatis-spring-boot-starter的org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration來(lái)實(shí)現(xiàn)多數(shù)據(jù)源注入的纫塌。在mybatis-spring-boot-starter:1.2.0中持搜,該類取消了默認(rèn)構(gòu)造函數(shù)十办,因此本項(xiàng)目依舊使用1.1.0版本芹助。需要關(guān)注后續(xù)版本是否會(huì)重新把擴(kuò)展開放處理励翼。

之所以依舊使用舊方案缚去,是我個(gè)人認(rèn)為開放擴(kuò)展是合理的路克,相信在未來(lái)的版本中會(huì)回歸织中。

增加主從庫(kù)配置(application.yml)

druid:type:com.alibaba.druid.pool.DruidDataSourcemaster:url:jdbc:mysql://192.168.249.128:3307/db-test?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=truedriver-class-name:com.mysql.jdbc.Driverusername:rootpassword:root? ? ? ? initial-size:5min-idle:1max-active:100test-on-borrow:trueslave:url:jdbc:mysql://192.168.249.128:3317/db-test?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8driver-class-name:com.mysql.jdbc.Driverusername:rootpassword:root? ? ? ? initial-size:5min-idle:1max-active:100test-on-borrow:true

創(chuàng)建數(shù)據(jù)源

@Configuration@EnableTransactionManagementpublicclassDataSourceConfiguration{@Value("${druid.type}")privateClass dataSourceType;@Bean(name ="masterDataSource")@Primary@ConfigurationProperties(prefix ="druid.master")publicDataSourcemasterDataSource(){returnDataSourceBuilder.create().type(dataSourceType).build();? ? }@Bean(name ="slaveDataSource")@ConfigurationProperties(prefix ="druid.slave")publicDataSourceslaveDataSource1(){returnDataSourceBuilder.create().type(dataSourceType).build();? ? }}

將多數(shù)據(jù)源注入到sqlSessionFactory中

前面提到了這里通過(guò)擴(kuò)展mybatis-spring-boot-starter的org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration來(lái)實(shí)現(xiàn)多數(shù)據(jù)源注入的

@Configuration@AutoConfigureAfter({DataSourceConfiguration.class})publicclassMybatisConfigurationextendsMybatisAutoConfiguration{privatestaticLog logger = LogFactory.getLog(MybatisConfiguration.class);@Resource(name ="masterDataSource")privateDataSource masterDataSource;@Resource(name ="slaveDataSource")privateDataSource slaveDataSource;@BeanpublicSqlSessionFactorysqlSessionFactory()throwsException{returnsuper.sqlSessionFactory(roundRobinDataSouceProxy());? ? }publicAbstractRoutingDataSourceroundRobinDataSouceProxy(){? ? ? ? ReadWriteSplitRoutingDataSource proxy =newReadWriteSplitRoutingDataSource();? ? ? ? Map targetDataResources =newClassLoaderRepository.SoftHashMap();? ? ? ? targetDataResources.put(DbContextHolder.DbType.MASTER,masterDataSource);? ? ? ? targetDataResources.put(DbContextHolder.DbType.SLAVE,slaveDataSource);? ? ? ? proxy.setDefaultTargetDataSource(masterDataSource);//默認(rèn)源proxy.setTargetDataSources(targetDataResources);returnproxy;? ? }}

實(shí)現(xiàn)讀寫分離(多數(shù)據(jù)源分離)

這里主要思路如下:

1-將不同的數(shù)據(jù)源標(biāo)識(shí)記錄在ThreadLocal中

2-通過(guò)注解標(biāo)識(shí)出當(dāng)前的service方法使用哪個(gè)庫(kù)

3-通過(guò)Spring AOP實(shí)現(xiàn)攔截注解并注入不同的標(biāo)識(shí)到threadlocal中

4-獲取源的時(shí)候通過(guò)threadlocal中不同的標(biāo)識(shí)給出不同的sqlSession

標(biāo)識(shí)存放ThreadLocal的實(shí)現(xiàn)

publicclassDbContextHolder{publicenumDbType{? ? ? ? MASTER,SLAVE? ? }privatestaticfinal ThreadLocal contextHolder =newThreadLocal<>();publicstaticvoidsetDbType(DbType dbType){if(dbType==null)thrownewNullPointerException();? ? ? ? contextHolder.set(dbType);? ? }publicstaticDbTypegetDbType(){returncontextHolder.get()==null?DbType.MASTER:contextHolder.get();? ? }publicstaticvoidclearDbType(){? ? ? ? contextHolder.remove();? ? }}

注解實(shí)現(xiàn)?遠(yuǎn)程抓娃娃開發(fā)找上海捌躍網(wǎng)絡(luò)科技有限公司

/**

* 該注解注釋在service方法上,標(biāo)注為鏈接slaves庫(kù)

* Created by Jason on 2017/3/6.

*/@Target({ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface ReadOnlyConnection {}

Spring AOP對(duì)注解的攔截

@Aspect@ComponentpublicclassReadOnlyConnectionInterceptorimplementsOrdered{publicstaticfinalLogger logger = LoggerFactory.getLogger(ReadOnlyConnectionInterceptor.class);@Around("@annotation(readOnlyConnection)")publicObjectproceed(ProceedingJoinPoint proceedingJoinPoint,ReadOnlyConnection readOnlyConnection)throwsThrowable{try{? ? ? ? ? ? logger.info("set database connection to read only");? ? ? ? ? ? DbContextHolder.setDbType(DbContextHolder.DbType.SLAVE);? ? ? ? ? ? Object result = proceedingJoinPoint.proceed();returnresult;? ? ? ? }finally{? ? ? ? ? ? DbContextHolder.clearDbType();? ? ? ? ? ? logger.info("restore database connection");? ? ? ? }? ? }@OverridepublicintgetOrder(){return0;? ? }}

根據(jù)標(biāo)識(shí)獲取不同源

這里我們通過(guò)擴(kuò)展AbstractRoutingDataSource來(lái)獲取不同的源衷戈。它是Spring提供的一個(gè)可以根據(jù)用戶發(fā)起的不同請(qǐng)求去轉(zhuǎn)換不同的數(shù)據(jù)源狭吼,比如根據(jù)用戶的不同地區(qū)語(yǔ)言選擇不同的數(shù)據(jù)庫(kù)。通過(guò)查看源碼可以發(fā)現(xiàn)殖妇,它是通過(guò)determineCurrentLookupKey()返回的不同key到sqlSessionFactory中獲取不同源(前面已經(jīng)展示了如何在sqlSessionFactory中注入多個(gè)源)

publicclassReadWriteSplitRoutingDataSourceextendsAbstractRoutingDataSource{@OverrideprotectedObjectdetermineCurrentLookupKey(){returnDbContextHolder.getDbType();? ? }}

以上就完成了讀寫分離(多數(shù)據(jù)源)的配置方案刁笙。下面是一個(gè)具體的實(shí)例

使用方式

Entity

@Table(name ="t_sys_dic_type")publicclassDicTypeextendsBaseEntity{? ? String code;? ? String name;? ? Integer status;? ? ...}

Mapper

publicinterfaceDicTypeMapperextendsBaseMapper{}

Service

@ServicepublicclassDicTypeService{@AutowiredprivateDicTypeMapper dicTypeMapper;@ReadOnlyConnectionpublicListgetAll(DicType dicType){if(dicType.getPage() !=null&& dicType.getRows() !=null) {? ? ? ? ? ? PageHelper.startPage(dicType.getPage(), dicType.getRows());? ? ? ? }returndicTypeMapper.selectAll();? ? }}

注意這里的@ReadOnlyConnection注解

Controller

@RestController@RequestMapping("/dictype")publicclassDicTypeController{@AutowiredprivateDicTypeService dicTypeService;@RequestMapping(value ="/all")publicPageInfogetALL(DicType dicType){? ? ? ? List dicTypeList = dicTypeService.getAll(dicType);returnnewPageInfo<>(dicTypeList);? ? }}

通過(guò)mvn spring-boot:run啟動(dòng)后

后臺(tái)打印出

c.a.d.m.ReadOnlyConnectionInterceptor : set database connection to read only

說(shuō)明使用了從庫(kù)的鏈接獲取數(shù)據(jù)

備注:如何保證多源事務(wù)呢?br/>1-在讀寫分離場(chǎng)景中不會(huì)考慮主從庫(kù)事務(wù),在純讀的上下文上使用@ReadOnlyConnection標(biāo)簽疲吸。其他則默認(rèn)使用主庫(kù)座每。

2-在多源場(chǎng)景中,Spring的@Transaction是可以保證多源的各自事務(wù)性的摘悴。

轉(zhuǎn)自:http://blog.51cto.com/13981400/2323314

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末峭梳,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蹂喻,更是在濱河造成了極大的恐慌葱椭,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件口四,死亡現(xiàn)場(chǎng)離奇詭異孵运,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蔓彩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門治笨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人赤嚼,你說(shuō)我怎么就攤上這事旷赖。” “怎么了更卒?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵杠愧,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我逞壁,道長(zhǎng)流济,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任腌闯,我火速辦了婚禮绳瘟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘姿骏。我一直安慰自己糖声,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布分瘦。 她就那樣靜靜地躺著蘸泻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪嘲玫。 梳的紋絲不亂的頭發(fā)上悦施,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音去团,去河邊找鬼抡诞。 笑死穷蛹,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的昼汗。 我是一名探鬼主播肴熏,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼顷窒!你這毒婦竟也來(lái)了蛙吏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤鞋吉,失蹤者是張志新(化名)和其女友劉穎鸦做,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體坯辩,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年崩侠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了漆魔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡却音,死狀恐怖改抡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情系瓢,我是刑警寧澤阿纤,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站夷陋,受9級(jí)特大地震影響欠拾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜骗绕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一藐窄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧酬土,春花似錦荆忍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至屈呕,卻和暖如春微宝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背虎眨。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工芥吟, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留侦铜,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓钟鸵,卻偏偏與公主長(zhǎng)得像钉稍,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子棺耍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,822評(píng)論 6 342
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理贡未,服務(wù)發(fā)現(xiàn),斷路器蒙袍,智...
    卡卡羅2017閱讀 134,659評(píng)論 18 139
  • 文|安羽心理 這兩天一下遇到兩位甲狀腺出問(wèn)題的個(gè)案俊卤,一位是甲狀腺癌,一位是甲狀腺瘤害幅。今天我們來(lái)聊一下關(guān)于“甲狀腺”...
    安羽心理閱讀 726評(píng)論 0 3
  • 第一次以一個(gè)小學(xué)生家長(zhǎng)身份參加女兒的家長(zhǎng)會(huì)消恍。感觸很深,以前都是自己上面講以现,這次換了個(gè)角色狠怨,感覺就是不同。 因?yàn)榉N種...
    瑩瀅如玉閱讀 143評(píng)論 0 1
  • 其實(shí)每個(gè)姑娘都是好姑娘邑遏。 獨(dú)身不是你不好佣赖,只是不愿將就。 讓自己整個(gè)人變得柔軟起來(lái)记盒,自帶光環(huán)憎蛤。 溫柔并不是說(shuō)話細(xì)聲...
    小胖蟲子閱讀 181評(píng)論 0 1