本文来自网易云社区

作者:王超

应用场景:项目中有一些报表统计与查询功能,对数据实时性要求不高,因此考虑对报表的统计与查询去操作slave db,减少对master的压力。

根据网上多份资料测试发现总是使用master数据源,无法切换到slave,经过多次调试修改现已完美通过,现整理下详细步骤和完整代码如下:

实现方式:配置多个数据源,使用Spring AOP实现拦截注解实现数据源的动态切换。

1. application.yml数据库配置:druid:

  1.   type: com.alibaba.druid.pool.DruidDataSource
  2.   master:
  3.     url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true
  4.     driver-class-name: com.mysql.jdbc.Driver
  5.     username: test
  6.     password: 123
  7.     initial-size: 5
  8.     max-active: 10
  9.     min-idle: 5
  10.     max-wait: 60000
  11.     time-between-eviction-runs-millis: 3000
  12.     min-evictable-idle-time-millis: 300000
  13.     validation-query: SELECT 'x' FROM DUAL
  14.     test-while-idle: true
  15.     test-on-borrow: true
  16.     test-on-return: false
  17.     filters: stat,wall,log4j2
  18.   slave:
  19.     url: jdbc:mysql://127.0.0.1:3307/test?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true
  20.     driver-class-name: com.mysql.jdbc.Driver
  21.     username: test
  22.     password: 123
  23.     initial-size: 5
  24.     max-active: 10
  25.     min-idle: 5
  26.     max-wait: 60000
  27.     time-between-eviction-runs-millis: 3000
  28.     min-evictable-idle-time-millis: 300000
  29.     validation-query: SELECT 'x' FROM DUAL
  30.     test-while-idle: true
  31.     test-on-borrow: true
  32.     test-on-return: false
  33.     filters: stat,wall,log4j2

2. 通过MybatisAutoConfiguration实现多数据源注入:

  1. @Configuration
  2. @EnableTransactionManagement
  3. public class DataSourceConfiguration extends MybatisAutoConfiguration {
  4.  
  5.     @Value("${druid.type}")
  6.     private Class<? extends DataSource> dataSourceType;
  7.  
  8.     @Bean(name = "masterDataSource")
  9.     @Primary
  10.     @ConfigurationProperties(prefix = "druid.master")
  11.     public DataSource masterDataSource(){
  12.         return DataSourceBuilder.create().type(dataSourceType).build();
  13.     }
  14.  
  15.     @Bean(name = "slaveDataSource")
  16.     @ConfigurationProperties(prefix = "druid.slave")
  17.     public DataSource slaveDataSource(){
  18.         return DataSourceBuilder.create().type(dataSourceType).build();
  19.     }
  20.  
  21.     @Bean
  22.     @Override
  23.     public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
  24.         return super.sqlSessionFactory(dataSource());
  25.     }
  26.  
  27.     @Bean(name = "dataSource")
  28.     public AbstractRoutingDataSource dataSource() {
  29.         MasterSlaveRoutingDataSource proxy = new MasterSlaveRoutingDataSource();
  30.         Map<Object, Object> targetDataResources = new HashMap<>();
  31.         targetDataResources.put(DbContextHolder.DbType.MASTER, masterDataSource());
  32.         targetDataResources.put(DbContextHolder.DbType.SLAVE, slaveDataSource());
  33.         proxy.setDefaultTargetDataSource(masterDataSource());
  34.         proxy.setTargetDataSources(targetDataResources);
  35.         proxy.afterPropertiesSet();
  36.         return proxy;
  37.     }
  38. }

3. 基于 AbstractRoutingDataSource 和 AOP 的多数据源的配置

  1. 我们自己定义一个DataSource类,来继承 AbstractRoutingDataSource
  2. public class MasterSlaveRoutingDataSource extends AbstractRoutingDataSource {
  3.     @Override
  4.     protected Object determineCurrentLookupKey() {
  5.         return DbContextHolder.getDbType();
  6.     }
  7. }这里通过determineCurrentLookupKey()返回的不同keysqlSessionFactory中获取对应数据源然后使用ThreadLocal来存放线程的变量,将不同的数据源标识记录在ThreadLocal
  8. public class DbContextHolder {
  9.     public enum DbType{
  10.         MASTER, SLAVE
  11.     }
  12.     private static final ThreadLocal<DbType> contextHolder = new ThreadLocal<>();
  13.     public static void setDbType(DbType dbType){
  14.         if (dbType==null) {
  15.             throw new NullPointerException();
  16.         }
  17.         contextHolder.set(dbType);
  18.     }
  19.     public static DbType getDbType(){
  20.         return contextHolder.get()==null?DbType.MASTER:contextHolder.get();
  21.     }
  22.     public static void clearDbType(){
  23.         contextHolder.remove();
  24.     }
  25. }

4. 注解实现

  1. @Target({ElementType.METHOD, ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface ReadOnlyConnection {
  4. }@Aspect
  5. @Component
  6. public class ReadOnlyConnectionInterceptor implements Ordered {
  7.     @Around("@annotation(readOnlyConnection)")
  8.     public Object proceed(ProceedingJoinPoint proceedingJoinPoint, ReadOnlyConnection readOnlyConnection) throws Throwable {
  9.         try {
  10.             DbContextHolder.setDbType(DbContextHolder.DbType.SLAVE);
  11.             Object result = proceedingJoinPoint.proceed();
  12.             return result;
  13.         }finally {
  14.             DbContextHolder.clearDbType();
  15.         }
  16.     }
  17.     @Override
  18.     public int getOrder() {
  19.         return 0;
  20.     }
  21. }

5. 应用方式:

  1. service层接口增加ReadOnlyConnection注解即可:
  2. @ReadOnlyConnectionpublic CommonPagingVO<GroupGoodsVO> 
  3. pagingByCondition(GroupGoodsCondition condition, int pageNum, int pageSize)
  4.  {  
  5.  Page<GroupGoodsVO> 
  6.  page = PageHelper.startPage(pageNum, pageSize).doSelectPage(() 
  7.  -> groupGoodsMapper.listByCondition(condition));   
  8.  return CommonPagingVO.get(page,page.getResult());
  9.  }
  10. 对于未加ReadOnlyConnection注解的默认使用masterDataSource

网易云免费体验馆,0成本体验20+款云产品!

更多网易研发、产品、运营经验分享请访问网易云社区

相关文章:
【推荐】 Foxman,基于微核架构的Mock解决方案

Spring Boot + Mybatis 多数据源配置实现读写分离的更多相关文章

  1. spring boot mybatis 多数据源配置

    package com.xynet.statistics.config.dataresources; import org.springframework.jdbc.datasource.lookup ...

  2. spring boot Mybatis多数据源配置

    关于 有时候,随着业务的发展,项目关联的数据来源会变得越来越复杂,使用的数据库会比较分散,这个时候就会采用多数据源的方式来获取数据.另外,多数据源也有其他好处,例如分布式数据库的读写分离,集成多种数据 ...

  3. spring boot mybatis多多数据源解决方法

    在我们的项目中不免会遇到需要在一个项目中使用多个数据源的问题,像我在得到一个任务将用户的聊天记录进行迁移的时候,就是用到了三个数据源,当时使用的AOP的编程方式根据访问的方法的不同进行动态的切换数据源 ...

  4. spring boot +mybatis(通过properties配置) 集成

    注:日常学习记录贴,下面描述的有误解的话请指出,大家一同学习. 因为我公司现在用的是postgresql数据库,所以我也用postgresql进行测试 一.前言 1.Spring boot 会默认读取 ...

  5. Spring MVC+Mybatis 多数据源配置

    文章来自:https://www.jianshu.com/p/fddcc1a6b2d8 1. 继承AbstractRoutingDataSource AbstractRoutingDataSource ...

  6. spring boot sharding-jdbc实现分佈式读写分离和分库分表的实现

    分布式读写分离和分库分表采用sharding-jdbc实现. sharding-jdbc是当当网推出的一款读写分离实现插件,其他的还有mycat,或者纯粹的Aop代码控制实现. 接下面用spring ...

  7. spring boot jpa 多数据源配置

    在实际项目中往往会使用2个数据源,这个时候就需要做额外的配置了.下面的配置在2.0.1.RELEASE 测试通过 1.配置文件 配置两个数据源 spring.datasource.url=jdbc:m ...

  8. spring boot + mybatis + druid配置实践

    最近开始搭建spring boot工程,将自身实践分享出来,本文将讲述spring boot + mybatis + druid的配置方案. pom.xml需要引入mybatis 启动依赖: < ...

  9. spring boot(四) 多数据源

    前言 前一篇中我们使用spring boot+mybatis创建了单一数据源,其中单一数据源不需要我们自己手动创建,spring boot自动配置在程序启动时会替我们创建好数据源. 准备工作 appl ...

随机推荐

  1. logback-spring.xml

    <?xml version="1.0" encoding="UTF-8"?><!--该日志将日志级别不同的log信息保存到不同的文件中--&g ...

  2. Android 调节图片工具类

    package com.base.changeimage; import android.graphics.Bitmap; import android.graphics.Canvas; import ...

  3. Ionic 2 中的创建一个闪视卡片组件

    闪视卡片是记忆信息的重要工具,它的使用可以追溯到19世纪.我们将要创建一个很酷的短暂动画来实现它.看起来像是这个样子的: 闪视卡片示例 Ionic 2 实例开发 新增章节将为你介绍如何在Ionic 2 ...

  4. 【js类库AngularJs】解决angular+springmvc的post提交问题

    [js类库AngularJs]解决angular+springmvc的post提交问题 AngularJS诞生于2009年,由Misko Hevery 等人创建,后为Google所收购.是一款优秀的前 ...

  5. C++拾遗(六)——复制控制

    年前忙了几天,到现在才算是有空休息下来.先祝大家新年快乐,心想事成:)我也会发笑脸o.o 这篇博文主要介绍定义一个类型的对象时的复制控制方式,这部分内容之前有一定的了解但又浅尝辄止,始终感觉没能找到要 ...

  6. jquery获取当前被选择的复选框的value的集合

    1.HTML代码 <input type="checkbox" name="productID" value="0"> < ...

  7. 一步一步教你玩转.NET Framework的配置文件app.config

    转自https://www.cnblogs.com/tonnie/archive/2010/12/17/appconfig.html 在一般的项目中,为了使你的代码更加灵活,更方便调整,减少不必要的h ...

  8. JavaSe-算数运算符

    算数运算符包括:+.-.*./.%.++.-- 代码: package com.java.chap02; public class Demo07 { public static void main(S ...

  9. codevs 4093 EZ的间谍网络

    时间限制: 10 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题目描述 Description 由于外国间谍的大量渗入,学校安全正处于高度的危机之中.YJY决定挺身而作出反抗 ...

  10. C++11 function用法 可调用对象模板类

    std::function<datatype()> ()内写参数类型 datatype 代表function的返回值 灵活的用法.. 代码如下 #include <stdio.h&g ...