新建一個(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