美文网首页技术方案
Spring Boot 集成Mybatis实现主从(多数据源)分

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

作者: 4a873e424089 | 来源:发表于2018-11-29 22:10 被阅读13次

    新建一个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

    相关文章

      网友评论

        本文标题:Spring Boot 集成Mybatis实现主从(多数据源)分

        本文链接:https://www.haomeiwen.com/subject/qspqcqtx.html