SpringBoot使用Mybatis實(shí)現(xiàn)主從分離的方法?針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。
成都創(chuàng)新互聯(lián)公司是專業(yè)的和平網(wǎng)站建設(shè)公司,和平接單;提供成都網(wǎng)站制作、網(wǎng)站設(shè)計(jì),網(wǎng)頁(yè)設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行和平網(wǎng)站開(kāi)發(fā)網(wǎng)頁(yè)制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛(ài)的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來(lái)合作!
新建一個(gè)Maven項(xiàng)目,最終項(xiàng)目結(jié)構(gòu)如下:
多數(shù)據(jù)源注入到sqlSessionFactory
POM增加如下依賴:
com.fasterxml.jackson.core jackson-core com.fasterxml.jackson.core jackson-databind com.fasterxml.jackson.datatype jackson-datatype-joda com.fasterxml.jackson.module jackson-module-parameter-names org.springframework.boot spring-boot-starter-jdbc MySQL mysql-connector-java com.alibaba druid 1.0.11 org.mybatis.spring.boot mybatis-spring-boot-starter 1.1.1 tk.mybatis mapper-spring-boot-starter 1.1.0 com.github.pagehelper pagehelper-spring-boot-starter 1.1.0 mybatis-spring-boot-starter org.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中,該類(lèi)取消了默認(rèn)構(gòu)造函數(shù),因此本項(xiàng)目依舊使用1.1.0版本。需要關(guān)注后續(xù)版本是否會(huì)重新把擴(kuò)展開(kāi)放處理。
之所以依舊使用舊方案,是我個(gè)人認(rèn)為開(kāi)放擴(kuò)展是合理的,相信在未來(lái)的版本中會(huì)回歸。
如果你需要其他方案可參考傳送門(mén)
增加主從庫(kù)配置(application.yml)
druid: type: com.alibaba.druid.pool.DruidDataSource master: url: jdbc:mysql://192.168.249.128:3307/db-test?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true driver-class-name: com.mysql.jdbc.Driver username: root password: root initial-size: 5 min-idle: 1 max-active: 100 test-on-borrow: true slave: url: jdbc:mysql://192.168.249.128:3317/db-test?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8 driver-class-name: com.mysql.jdbc.Driver username: root password: root initial-size: 5 min-idle: 1 max-active: 100 test-on-borrow: true
創(chuàng)建數(shù)據(jù)源
@Configuration @EnableTransactionManagement public class DataSourceConfiguration { @Value("${druid.type}") private Class<? extends DataSource> dataSourceType; @Bean(name = "masterDataSource") @Primary @ConfigurationProperties(prefix = "druid.master") public DataSource masterDataSource(){ return DataSourceBuilder.create().type(dataSourceType).build(); } @Bean(name = "slaveDataSource") @ConfigurationProperties(prefix = "druid.slave") public DataSource slaveDataSource1(){ return DataSourceBuilder.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}) public class MybatisConfiguration extends MybatisAutoConfiguration { private static Log logger = LogFactory.getLog(MybatisConfiguration.class); @Resource(name = "masterDataSource") private DataSource masterDataSource; @Resource(name = "slaveDataSource") private DataSource slaveDataSource; @Bean public SqlSessionFactory sqlSessionFactory() throws Exception { return super.sqlSessionFactory(roundRobinDataSouceProxy()); } public AbstractRoutingDataSource roundRobinDataSouceProxy(){ ReadWriteSplitRoutingDataSource proxy = new ReadWriteSplitRoutingDataSource(); Map
實(shí)現(xiàn)讀寫(xiě)分離(多數(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)
public class DbContextHolder { public enum DbType{ MASTER,SLAVE } private static final ThreadLocalcontextHolder = new ThreadLocal<>(); public static void setDbType(DbType dbType){ if(dbType==null)throw new NullPointerException(); contextHolder.set(dbType); } public static DbType getDbType(){ return contextHolder.get()==null?DbType.MASTER:contextHolder.get(); } public static void clearDbType(){ contextHolder.remove(); } }
注解實(shí)現(xiàn)
/** * 該注解注釋在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 @Component public class ReadOnlyConnectionInterceptor implements Ordered { public static final Logger logger = LoggerFactory.getLogger(ReadOnlyConnectionInterceptor.class); @Around("@annotation(readOnlyConnection)") public Object proceed(ProceedingJoinPoint proceedingJoinPoint,ReadOnlyConnection readOnlyConnection) throws Throwable { try { logger.info("set database connection to read only"); DbContextHolder.setDbType(DbContextHolder.DbType.SLAVE); Object result = proceedingJoinPoint.proceed(); return result; }finally { DbContextHolder.clearDbType(); logger.info("restore database connection"); } } @Override public int getOrder() { return 0; } }
根據(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è)源)
public class ReadWriteSplitRoutingDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DbContextHolder.getDbType(); } }
以上就完成了讀寫(xiě)分離(多數(shù)據(jù)源)的配置方案。下面是一個(gè)具體的實(shí)例
使用方式
Entity
@Table(name = "t_sys_dic_type") public class DicType extends BaseEntity{ String code; String name; Integer status; ... }
Mapper
public interface DicTypeMapper extends BaseMapper{ }
Service
@Service public class DicTypeService { @Autowired private DicTypeMapper dicTypeMapper; @ReadOnlyConnection public ListgetAll(DicType dicType){ if (dicType.getPage() != null && dicType.getRows() != null) { PageHelper.startPage(dicType.getPage(), dicType.getRows()); } return dicTypeMapper.selectAll(); } }
注意這里的@ReadOnlyConnection注解
Controller
@RestController @RequestMapping("/dictype") public class DicTypeController { @Autowired private DicTypeService dicTypeService; @RequestMapping(value = "/all") public PageInfogetALL(DicType dicType){ List dicTypeList = dicTypeService.getAll(dicType); return new PageInfo<>(dicTypeList); } }
通過(guò)mvn spring-boot:run啟動(dòng)后,即可通過(guò)http://localhost:9090/dictype/all 獲取到數(shù)據(jù)
后臺(tái)打印出
c.a.d.m.ReadOnlyConnectionInterceptor : set database connection to read only
說(shuō)明使用了從庫(kù)的鏈接獲取數(shù)據(jù)
備注:如何保證多源事務(wù)呢?
1-在讀寫(xiě)分離場(chǎng)景中不會(huì)考慮主從庫(kù)事務(wù),在純讀的上下文上使用@ReadOnlyConnection標(biāo)簽。其他則默認(rèn)使用主庫(kù)。
2-在多源場(chǎng)景中,Spring的@Transaction是可以保證多源的事務(wù)性的。
關(guān)于SpringBoot使用Mybatis實(shí)現(xiàn)主從分離的方法問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒(méi)有解開(kāi),可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識(shí)。