Spring Boot 集成Mybatis实现主从(多数据源)分离方案

新建一个Maven项目,最终项目结构如下:

多数据源注入到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

这里需要注意的是:项目是通过扩展mybatis-spring-boot-starter的org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration来实现多数据源注入的。在mybatis-spring-boot-starter:1.2.0中,该类取消了默认构造函数,因此本项目依旧使用1.1.0版本。需要关注后续版本是否会重新把扩展开放处理。

之所以依旧使用旧方案,是我个人认为开放扩展是合理的,相信在未来的版本中会回归。

增加主从库配置(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

创建数据源

@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();    }}

将多数据源注入到sqlSessionFactory中

前面提到了这里通过扩展mybatis-spring-boot-starter的org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration来实现多数据源注入的

@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);//默认源proxy.setTargetDataSources(targetDataResources);returnproxy;    }}

实现读写分离(多数据源分离)

这里主要思路如下:

1-将不同的数据源标识记录在ThreadLocal中

2-通过注解标识出当前的service方法使用哪个库

3-通过Spring AOP实现拦截注解并注入不同的标识到threadlocal中

4-获取源的时候通过threadlocal中不同的标识给出不同的sqlSession

标识存放ThreadLocal的实现

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();    }}

注解实现 远程抓娃娃开发找上海捌跃网络科技有限公司

/**

* 该注解注释在service方法上,标注为链接slaves库

* Created by Jason on 2017/3/6.

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

Spring AOP对注解的拦截

@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;    }}

根据标识获取不同源

这里我们通过扩展AbstractRoutingDataSource来获取不同的源。它是Spring提供的一个可以根据用户发起的不同请求去转换不同的数据源,比如根据用户的不同地区语言选择不同的数据库。通过查看源码可以发现,它是通过determineCurrentLookupKey()返回的不同key到sqlSessionFactory中获取不同源(前面已经展示了如何在sqlSessionFactory中注入多个源)

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

以上就完成了读写分离(多数据源)的配置方案。下面是一个具体的实例

使用方式

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);    }}

通过mvn spring-boot:run启动后

后台打印出

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

说明使用了从库的链接获取数据

备注:如何保证多源事务呢?br/>1-在读写分离场景中不会考虑主从库事务,在纯读的上下文上使用@ReadOnlyConnection标签。其他则默认使用主库。

2-在多源场景中,Spring的@Transaction是可以保证多源的各自事务性的。

转自:http://blog.51cto.com/13981400/2323314

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,724评论 6 342
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,585评论 18 139
  • 文|安羽心理 这两天一下遇到两位甲状腺出问题的个案,一位是甲状腺癌,一位是甲状腺瘤。今天我们来聊一下关于“甲状腺”...
    安羽心理阅读 706评论 0 3
  • 第一次以一个小学生家长身份参加女儿的家长会。感触很深,以前都是自己上面讲,这次换了个角色,感觉就是不同。 因为种种...
    莹滢如玉阅读 139评论 0 1
  • 其实每个姑娘都是好姑娘。 独身不是你不好,只是不愿将就。 让自己整个人变得柔软起来,自带光环。 温柔并不是说话细声...
    小胖虫子阅读 172评论 0 1