SpringBoot+Mybatis-Plus實(shí)現(xiàn)mysql讀寫分離方案的示例代碼
1. 引入mybatis-plus相關(guān)包,pom.xml文件

2. 配置文件application.property增加多庫配置
mysql 數(shù)據(jù)源配置
spring.datasource.primary.jdbc-url=jdbc:mysql://xx.xx.xx.xx:3306/portal?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&characterEncoding=utf8&serverTimezone=GMT%2B8 spring.datasource.primary.username=root spring.datasource.primary.password=root spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver #mysql slave 數(shù)據(jù)源配置 spring.datasource.slave.jdbc-url=jdbc:mysql://xx.xx.xx.xx:3306/portal?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC&characterEncoding=utf8&serverTimezone=GMT%2B8 spring.datasource.slave.username=root spring.datasource.slave.password=root spring.datasource.slave.driver-class-name=com.mysql.cj.jdbc.Driver
3. 配置數(shù)據(jù)源及注解
數(shù)據(jù)源配置 MultiDataSourceConfig.Java
/**
* 配置多數(shù)據(jù)源
*/
@Profile("dev")//開發(fā)模式配置文件
@Configuration
@MapperScan(basePackages = "com.csc.portal.mapper")//掃描包
public class MultiDataSourceConfig {
/**
* 主數(shù)據(jù)源
* @return
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
/**
* 從數(shù)據(jù)源
* @return
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
/**
* 路由數(shù)據(jù)源,前面兩個(gè)數(shù)據(jù)源是為了創(chuàng)建此數(shù)據(jù)源
* @param masterDataSource 主數(shù)據(jù)源
* @param slaveDataSource 從數(shù)據(jù)源
* @return
*/
@Bean
public DataSource myRoutingDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slaveDataSource") DataSource slaveDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DBTypeEnum.MASTER, masterDataSource);
targetDataSources.put(DBTypeEnum.SLAVE, slaveDataSource);
MyRoutingDataSource myRoutingDataSource = new MyRoutingDataSource();
myRoutingDataSource.setDefaultTargetDataSource(slaveDataSource);//設(shè)置默認(rèn)數(shù)據(jù)源
myRoutingDataSource.setTargetDataSources(targetDataSources);//設(shè)置路由表,使用map的key,value方式得到對應(yīng)數(shù)據(jù)源
return myRoutingDataSource;
}
數(shù)據(jù)庫枚舉類
public enum DBTypeEnum {
MASTER, SLAVE;
}
注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Master {
}
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Slave {
}
4. Mybatis-plus配置
@EnableTransactionManagement
@Configuration
@MapperScan(basePackages = "com.csc.portal.mapper")
public class MybatisPlusConfig {
/**
* 分頁插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
@Resource(name = "myRoutingDataSource")
private DataSource myRoutingDataSource;
/**
* 使用MyBatis Plus的sqlSessionFactory代替,
* 此處注意mybatis與mybatisPlus的配置不同,不然掃描不到對數(shù)據(jù)操作的方法。會報(bào)未綁定錯(cuò)誤
* @return sqlSessionFactory
* @throws Exception
*/
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(myRoutingDataSource);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
MybatisConfiguration mybatisConfiguration = new MybatisConfiguration();
sqlSessionFactoryBean.setConfiguration(mybatisConfiguration);
return sqlSessionFactoryBean.getObject();
}
/**
* 此處為使用mybatis時(shí)的sqlsessionFactory配置
* @return
* @throws Exception
*/
/*
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(myRoutingDataSource);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
return sqlSessionFactoryBean.getObject();
}
*/
/**
* 事務(wù)配置
* @return 事務(wù)管理器
*/
@Bean
public DataSourceTransactionManager transactionManager() {
DataSourceTransactionManager tx = new DataSourceTransactionManager();
tx.setDataSource(myRoutingDataSource);
return tx;
}
5. 增加數(shù)據(jù)源管理類
DBContextHolder.java
public class DBContextHolder {
/**
* 外部一個(gè)請求將會產(chǎn)生一個(gè)線程與之對應(yīng),每個(gè)線程的變量可用ThreadLocal進(jìn)行存儲
*/
private static final ThreadLocal<DBTypeEnum> contextHolder = new ThreadLocal<>();
public static void set(DBTypeEnum dbType) {
contextHolder.set(dbType);
}
public static DBTypeEnum get() {
return contextHolder.get();
}
public static void master() {
set(DBTypeEnum.MASTER);
System.out.println("切換到master");
}
public static void slave() {
set(DBTypeEnum.SLAVE);
System.out.println("切換到slave");
}
}
指定選擇數(shù)據(jù)源
MyRoutingDataSource.java 方法determineCurrentLookupKey決定最終使用哪個(gè)數(shù)據(jù)源進(jìn)行操作,若為空則使用默認(rèn)數(shù)據(jù)源。
public class MyRoutingDataSource extends AbstractRoutingDataSource {
@Nullable
@Override
protected Object determineCurrentLookupKey() {
System.out.println("線程名:"+Thread.currentThread().getName()+":"+DBContextHolder.get());
return DBContextHolder.get();
/* if (DBContextHolder.get() != null) {
System.out.println("線程名:"+Thread.currentThread().getName()+":"+DBContextHolder.get());
return DBContextHolder.get();
} else {
System.out.println("未匹配到指定數(shù)據(jù)庫,默認(rèn)切換到Master");
return DBTypeEnum.MASTER;
}*/
//return DBContextHolder.get();
}
}
6. 增加aop切面
@Aspect
@Component
@Order(0)//配置注解優(yōu)先級,優(yōu)于事物注解@Transactional先進(jìn)行數(shù)據(jù)源切換,
//不然在事物中進(jìn)行數(shù)據(jù)源切換無效
public class DataSourceAop {
@Pointcut(/*"!@annotation(com.csc.portal.annotation.Master) " +
"&& (execution(* com.csc.portal.service..*.select*(..)) " +
"|| execution(* com.csc.portal.service..*.get*(..))"+*/
" @annotation(com.csc.portal.annotation.Slave)")
public void readPointcut() {
}
@Pointcut("@annotation(com.csc.portal.annotation.Master) " //+
/* "|| execution(* com.csc.portal.service..*.insert*(..)) " +
"|| execution(* com.csc.portal.service..*.add*(..)) " +
"|| execution(* com.csc.portal.service..*.update*(..)) " +
"|| execution(* com.csc.portal.service..*.edit*(..)) " +
"|| execution(* com.csc.portal.service..*.delete*(..)) " +
"|| execution(* com.csc.portal.service..*.remove*(..))"*/)
public void writePointcut() {
}
@Before("readPointcut()")
public void read() {
//獲取攔截類
DBContextHolder.slave();
System.out.println(Thread.currentThread().getName()+DBContextHolder.get());
}
@Before("writePointcut()")
public void write() {
//獲取攔截類
/* String className = pjp.getTarget().getClass().getName();
System.out.println("當(dāng)前線程"+Thread.currentThread().getName()+" 攔截類為:" + className);
//獲取攔截的方法名
MethodSignature msig = (MethodSignature) pjp.getSignature();
Method currentMethod = null;
try {
currentMethod = pjp.getTarget().getClass().getMethod(msig.getName(), msig.getParameterTypes());
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
String methodName = currentMethod.getName();
System.out.println("攔截方法名為:" + methodName);*/
DBContextHolder.master();
System.out.println(Thread.currentThread().getName()+DBContextHolder.get());
}
}
6. 實(shí)際應(yīng)用
- 在service層方法前增加注解@Master表示使用主庫,進(jìn)行增刪改的操作使用主庫。
- 在service層方法前增加注解@Slave表示使用從庫,進(jìn)行查的操作使用從庫,默認(rèn)使用從庫,可不配置。
- @ Transactional注解加到service層,增加了@Transactional注解后,啟用事務(wù)后,一個(gè)事務(wù)內(nèi)部的connection是復(fù)用的,所以就算AOP切了數(shù)據(jù)源字符串,但是數(shù)據(jù)源并不會被真正修改。所以@Transactional注解不要寫在controller層,不然在service層也切換不了數(shù)據(jù)源。
- @Transactional與@Master可同時(shí)使用,已經(jīng)配置@Master注解的優(yōu)先級較高,先切換數(shù)據(jù)源后執(zhí)行事務(wù)。
到此這篇關(guān)于SpringBoot+Mybatis-Plus實(shí)現(xiàn)mysql讀寫分離方案的示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot Mybatis-Plus mysql讀寫分離內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- mysql?中的備份恢復(fù),分區(qū)分表,主從復(fù)制,讀寫分離
- 最新MySql8.27主從復(fù)制及SpringBoot項(xiàng)目中的讀寫分離實(shí)戰(zhàn)教程
- MySQL讀寫分離原理詳細(xì)解析
- 分享MySQL?主從延遲與讀寫分離的七種解決方案
- springboot結(jié)合mysql主從來實(shí)現(xiàn)讀寫分離的方法示例
- Springboot + Mysql8實(shí)現(xiàn)讀寫分離功能
- springboot基于Mybatis mysql實(shí)現(xiàn)讀寫分離
- MySQL 讀寫分離的實(shí)現(xiàn)邏輯及步驟詳解
相關(guān)文章
Seata AT模式TransactionHook被刪除探究
這篇文章主要為大家介紹了Seata AT模式TransactionHook被刪除探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
PowerJob的TimingStrategyHandler工作流程源碼解讀
這篇文章主要為大家介紹了PowerJob的TimingStrategyHandler工作流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01
Java設(shè)計(jì)模式之原型設(shè)計(jì)示例詳解
這篇文章主要為大家詳細(xì)介紹了Java的原型設(shè)計(jì)模式,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-03-03
Mybatis事務(wù)如何跟Spring結(jié)合(數(shù)據(jù)庫事務(wù)特性和Spring事務(wù)管理源碼)
MyBatis與Spring的事務(wù)結(jié)合主要是通過Spring的事務(wù)管理和MyBatis的數(shù)據(jù)庫操作來實(shí)現(xiàn)的,在本文中,我們將從數(shù)據(jù)庫事務(wù)特性和Spring事務(wù)管理源碼兩個(gè)角度來分析MyBatis事務(wù)如何與Spring結(jié)合到一起的原理,感興趣的朋友一起看看吧2024-01-01
多線程Thread,Runnable,Callable實(shí)現(xiàn)方式
這篇文章主要為大家詳細(xì)介紹了Java多線程如何實(shí)現(xiàn)Thread,Runnable,Callable的方式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
Java中常見延時(shí)隊(duì)列的實(shí)現(xiàn)方案小結(jié)(建議收藏)
延時(shí)隊(duì)列它要具有隊(duì)列的特性,再給它附加一個(gè)延遲消費(fèi)隊(duì)列消息的功能,也就是說可以指定隊(duì)列中的消息在哪個(gè)時(shí)間點(diǎn)被消費(fèi),這篇文章主要介紹了Java中常見延時(shí)隊(duì)列的實(shí)現(xiàn)方案總結(jié),需要的朋友可以參考下2024-04-04

