SpringBoot+Mybatis-Plus實現(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ù)源,前面兩個數(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ù)操作的方法。會報未綁定錯誤
* @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時的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 {
/**
* 外部一個請求將會產(chǎn)生一個線程與之對應(yīng),每個線程的變量可用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決定最終使用哪個數(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. 實際應(yīng)用
- 在service層方法前增加注解@Master表示使用主庫,進(jìn)行增刪改的操作使用主庫。
- 在service層方法前增加注解@Slave表示使用從庫,進(jìn)行查的操作使用從庫,默認(rèn)使用從庫,可不配置。
- @ Transactional注解加到service層,增加了@Transactional注解后,啟用事務(wù)后,一個事務(wù)內(nèi)部的connection是復(fù)用的,所以就算AOP切了數(shù)據(jù)源字符串,但是數(shù)據(jù)源并不會被真正修改。所以@Transactional注解不要寫在controller層,不然在service層也切換不了數(shù)據(jù)源。
- @Transactional與@Master可同時使用,已經(jīng)配置@Master注解的優(yōu)先級較高,先切換數(shù)據(jù)源后執(zhí)行事務(wù)。
到此這篇關(guān)于SpringBoot+Mybatis-Plus實現(xiàn)mysql讀寫分離方案的示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot Mybatis-Plus mysql讀寫分離內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 詳解循環(huán)屏障CyclicBarrier如何實現(xiàn)多線程分段等待執(zhí)行完成
CyclicBarrier是一個同步工具類,可以翻譯成循環(huán)屏障,也叫障礙器或同步屏障。CyclicBarrier內(nèi)部有一個計數(shù)器count,調(diào)用障礙器的await方法會使計數(shù)器count的值減一,當(dāng)計數(shù)器count的值為0時,表明調(diào)用了await方法線程已經(jīng)達(dá)到了設(shè)置的數(shù)量2021-11-11
Java任意長度byte數(shù)組轉(zhuǎn)換為int數(shù)組的方法
這篇文章主要給大家介紹了關(guān)于Java任意長度byte數(shù)組轉(zhuǎn)換為int數(shù)組的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者使用Java具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
Mybatis Plus 增刪改查的實現(xiàn)(小白教程)
本文主要介紹了Mybatis Plus 增刪改查,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09
解讀CommandLineRunner或者ApplicationRunner接口
這篇文章主要介紹了解讀CommandLineRunner或者ApplicationRunner接口的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-02-02

