温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

SpringBoot+Mybatis-Plus实现mysql读写分离方案的示例代码

发布时间:2021-03-09 15:41:33 来源:亿速云 阅读:860 作者:小新 栏目:开发技术

这篇文章主要介绍了SpringBoot+Mybatis-Plus实现mysql读写分离方案的示例代码,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

1. 引入mybatis-plus相关包,pom.xml文件

SpringBoot+Mybatis-Plus实现mysql读写分离方案的示例代码

2. 配置文件application.property增加多库配置

mysql 数据源配置

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 数据源配置 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. 配置数据源及注解

数据源配置 MultiDataSourceConfig.Java

/**  * 配置多数据源  */ @Profile("dev")//开发模式配置文件 @Configuration @MapperScan(basePackages = "com.csc.portal.mapper")//扫描包 public class MultiDataSourceConfig {   /**    * 主数据源    * @return    */   @Bean   @ConfigurationProperties(prefix = "spring.datasource.primary")   public DataSource masterDataSource() {     return DataSourceBuilder.create().build();   }   /**    * 从数据源    * @return    */   @Bean   @ConfigurationProperties(prefix = "spring.datasource.slave")   public DataSource slaveDataSource() {     return DataSourceBuilder.create().build();   }   /**    * 路由数据源,前面两个数据源是为了创建此数据源    * @param masterDataSource 主数据源    * @param slaveDataSource 从数据源    * @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);//设置默认数据源     myRoutingDataSource.setTargetDataSources(targetDataSources);//设置路由表,使用map的key,value方式得到对应数据源     return myRoutingDataSource;   }

数据库枚举类

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的配置不同,不然扫描不到对数据操作的方法。会报未绑定错误   * @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. 增加数据源管理类

DBContextHolder.java

public class DBContextHolder {   /**    * 外部一个请求将会产生一个线程与之对应,每个线程的变量可用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");   } }

指定选择数据源

MyRoutingDataSource.java 方法determineCurrentLookupKey决定最终使用哪个数据源进行操作,若为空则使用默认数据源。

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("未匹配到指定数据库,默认切换到Master");       return DBTypeEnum.MASTER;     }*/     //return DBContextHolder.get();   } }

6. 增加aop切面

@Aspect @Component @Order(0)//配置注解优先级,优于事物注解@Transactional先进行数据源切换, //不然在事物中进行数据源切换无效 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. 实际应用

  1. 在service层方法前增加注解@Master表示使用主库,进行增删改的操作使用主库。

  2. 在service层方法前增加注解@Slave表示使用从库,进行查的操作使用从库,默认使用从库,可不配置。

  3. @ Transactional注解加到service层,增加了@Transactional注解后,启用事务后,一个事务内部的connection是复用的,所以就算AOP切了数据源字符串,但是数据源并不会被真正修改。所以@Transactional注解不要写在controller层,不然在service层也切换不了数据源。

  4. @Transactional与@Master可同时使用,已经配置@Master注解的优先级较高,先切换数据源后执行事务。

感谢你能够认真阅读完这篇文章,希望小编分享的“SpringBoot+Mybatis-Plus实现mysql读写分离方案的示例代码”这篇文章对大家有帮助,同时也希望大家多多支持亿速云,关注亿速云行业资讯频道,更多相关知识等着你来学习!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI