SpringBoot+Mybatis-Plus實現(xiàn)mysql讀寫分離方案的示例代碼
1. 引入mybatis-plus相關包,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ù)據(jù)源 myRoutingDataSource.setTargetDataSources(targetDataSources);//設置路由表,使用map的key,value方式得到對應數(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(); } */ /** * 事務配置 * @return 事務管理器 */ @Bean public DataSourceTransactionManager transactionManager() { DataSourceTransactionManager tx = new DataSourceTransactionManager(); tx.setDataSource(myRoutingDataSource); return tx; }
5. 增加數(shù)據(jù)源管理類
DBContextHolder.java
public class DBContextHolder { /** * 外部一個請求將會產(chǎn)生一個線程與之對應,每個線程的變量可用ThreadLocal進行存儲 */ 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ù)源進行操作,若為空則使用默認數(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ù)庫,默認切換到Master"); return DBTypeEnum.MASTER; }*/ //return DBContextHolder.get(); } }
6. 增加aop切面
@Aspect @Component @Order(0)//配置注解優(yōu)先級,優(yōu)于事物注解@Transactional先進行數(shù)據(jù)源切換, //不然在事物中進行數(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("當前線程"+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. 實際應用
- 在service層方法前增加注解@Master表示使用主庫,進行增刪改的操作使用主庫。
- 在service層方法前增加注解@Slave表示使用從庫,進行查的操作使用從庫,默認使用從庫,可不配置。
- @ Transactional注解加到service層,增加了@Transactional注解后,啟用事務后,一個事務內部的connection是復用的,所以就算AOP切了數(shù)據(jù)源字符串,但是數(shù)據(jù)源并不會被真正修改。所以@Transactional注解不要寫在controller層,不然在service層也切換不了數(shù)據(jù)源。
- @Transactional與@Master可同時使用,已經(jīng)配置@Master注解的優(yōu)先級較高,先切換數(shù)據(jù)源后執(zhí)行事務。
到此這篇關于SpringBoot+Mybatis-Plus實現(xiàn)mysql讀寫分離方案的示例代碼的文章就介紹到這了,更多相關SpringBoot Mybatis-Plus mysql讀寫分離內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Seata AT模式TransactionHook被刪除探究
這篇文章主要為大家介紹了Seata AT模式TransactionHook被刪除探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11PowerJob的TimingStrategyHandler工作流程源碼解讀
這篇文章主要為大家介紹了PowerJob的TimingStrategyHandler工作流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2024-01-01Mybatis事務如何跟Spring結合(數(shù)據(jù)庫事務特性和Spring事務管理源碼)
MyBatis與Spring的事務結合主要是通過Spring的事務管理和MyBatis的數(shù)據(jù)庫操作來實現(xiàn)的,在本文中,我們將從數(shù)據(jù)庫事務特性和Spring事務管理源碼兩個角度來分析MyBatis事務如何與Spring結合到一起的原理,感興趣的朋友一起看看吧2024-01-01多線程Thread,Runnable,Callable實現(xiàn)方式
這篇文章主要為大家詳細介紹了Java多線程如何實現(xiàn)Thread,Runnable,Callable的方式,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-08-08Java中常見延時隊列的實現(xiàn)方案小結(建議收藏)
延時隊列它要具有隊列的特性,再給它附加一個延遲消費隊列消息的功能,也就是說可以指定隊列中的消息在哪個時間點被消費,這篇文章主要介紹了Java中常見延時隊列的實現(xiàn)方案總結,需要的朋友可以參考下2024-04-04