Spring-Mybatis读写分离笔记整理

  1. 编写Spring动态数据源实现类
    public class ReadWriteDataSource extends AbstractRoutingDataSource {
         
        @Nullable
        @Override
        protected Object determineCurrentLookupKey() {
         
            return DsTypeHolder.get().getCode();
        }
    }
    
  2. 编写Mybatis拦截器切换数据源
    @Intercepts({
         
       @Signature(type = Executor.class, method = "update",
               args = {
         MappedStatement.class, Object.class}),
       @Signature(type = Executor.class, method = "query",
               args = {
         MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
       @Signature(type = Executor.class, method = "query",
               args = {
         MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})
    })
    public class DynamicDataSourceInterceptor implements Interceptor {
         
        private static final String REGEX = ".*insert\\u0020.*|.*delete\\u0020.*|.*update\\u0020.*" ;
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
         
            boolean transactionActive = TransactionSynchronizationManager.isActualTransactionActive();
            DsType dsType = DsType.MASTER;
            if (!transactionActive){
         
                Object[] args = invocation.getArgs();
                MappedStatement ms = (MappedStatement) args[0] ;
                if (ms.getSqlCommandType().equals(SqlCommandType.SELECT)){
         
                    // 如果selectKey为自增id查询主键,使用主库
                    if (ms.getId().contains(SelectKeyGenerator.SELECT_KEY_SUFFIX)){
         
                        dsType = DsType.MASTER;
                    }else {
         
                        BoundSql boundSql = ms.getSqlSource().getBoundSql(args[1]);
                        String sql = boundSql.getSql().toLowerCase(Locale.CHINA).replaceAll("[\\t\\n\\r]]", "");
                        // 如果是更新操作则切换到主库
                        if (sql.matches(REGEX)){
         
                            dsType = DsType.MASTER;
                        }else {
         
                            // 非更新操作则使用从库
                            dsType = DsType.SLAVE;
                        }
                    }
                }
            }else {
         
                dsType = DsType.MASTER;
            }
            DsTypeHolder.set(dsType);
            return invocation.proceed();
        }
        @Override
        public Object plugin(Object target) {
         
            if (target instanceof Executor){
         
                return Plugin.wrap(target, this);
            }
            return target ;
        }
    }
    
  3. 数据源切换工具类
    @Getter
    public enum DsType {
         
        MASTER("master"),
        SLAVE("slave");
    
        private final String code ;
    
        DsType(String code){
         
            this.code = code ;
        }
    }
    public class DsTypeHolder {
         
        private static ThreadLocal<DsType> dsTypeThreadLocal = new ThreadLocal<>();
    
        public static void set(DsType dsType) {
         
            dsTypeThreadLocal.set(dsType);
        }
    
        public static DsType get() {
         
            if (dsTypeThreadLocal.get() == null){
         
                return DsType.MASTER ;
            }
            return dsTypeThreadLocal.get();
        }
    
        public static void clear() {
         
            dsTypeThreadLocal.remove();
        }
    }
    
  4. 编写数据源配置
    spring:
      application:
        name: hello-read-write-app
      jackson:
        time-zone: GMT+8
      datasource:
        master:
          driver-class-name: com.mysql.cj.jdbc.Driver
          jdbc-url: jdbc:mysql://127.0.0.1:3306/test
          username: root
          password: root
        slave:
          driver-class-name: com.mysql.cj.jdbc.Driver
          jdbc-url: jdbc:mysql://127.0.0.1:3306/test
          username: root
          password: root
    logging:
      level:
        root: info
        com.yicj.study.rw.repository.mapper: debug
    mybatis-plus:
      mapper-locations: classpath:/mapper/*Mapper.xml
    
  5. 将数据源注入到spring容器中
    @Configuration
    public class DataSourceConfig {
         
        @Bean
        @ConfigurationProperties("spring.datasource.master")
        public DataSource masterDataSource() {
         
            return DataSourceBuilder.create().build();
        }
    
        @Bean
        @ConfigurationProperties("spring.datasource.slave")
        public DataSource slaveDataSource() {
         
            return DataSourceBuilder.create().build();
        }
    
        @Bean
        public ReadWriteDataSource readWriteDataSource(
                @Qualifier("masterDataSource") DataSource masterDataSource,
                @Qualifier("slaveDataSource") DataSource slaveDataSource) {
         
            Map<Object, Object> targetDataSources = new HashMap<>();
            targetDataSources.put(DsType.MASTER.getCode(), masterDataSource);
            targetDataSources.put(DsType.SLAVE.getCode(), slaveDataSource);
            //
            ReadWriteDataSource readWriteDataSource = new ReadWriteDataSource();
            readWriteDataSource.setDefaultTargetDataSource(masterDataSource);
            readWriteDataSource.setTargetDataSources(targetDataSources);
            return readWriteDataSource;
        }
    }
    
  6. Mybatis-plus配置
    @Configuration
    public class MyBatisConfig {
         
    
        @Autowired
        private ReadWriteDataSource readWriteDataSource ;
    
        @Bean
        public SqlSessionFactory sqlSessionFactory() throws Exception {
         
            MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
            sqlSessionFactory.setDataSource(readWriteDataSource);
            MybatisConfiguration configuration = new MybatisConfiguration();
            configuration.setJdbcTypeForNull(JdbcType.NULL);
            configuration.setMapUnderscoreToCamelCase(true);
            configuration.setCacheEnabled(false);
            // 动态数据源拦截器配置
            configuration.addInterceptor(new DynamicDataSourceInterceptor());
            //如果配置多个插件,切记分页最后添加
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
            configuration.addInterceptor(interceptor);
            sqlSessionFactory.setConfiguration(configuration);
            return sqlSessionFactory.getObject();
        }
    
        @Bean
        public PlatformTransactionManager platformTransactionManager() {
         
            return new DataSourceTransactionManager(readWriteDataSource);
        }
    }
    

相关推荐

  1. Spring-Mybatis分离笔记整理

    2023-12-05 15:28:07       27 阅读
  2. spring boot+mybatis-plus配置分离

    2023-12-05 15:28:07       30 阅读
  3. mysql分离

    2023-12-05 15:28:07       31 阅读

最近更新

  1. TCP协议是安全的吗?

    2023-12-05 15:28:07       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-05 15:28:07       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-05 15:28:07       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-05 15:28:07       18 阅读

热门阅读

  1. PTA 7-238 整数转换为字符串

    2023-12-05 15:28:07       37 阅读
  2. 2023-简单点-tkinter中的ttk和tk

    2023-12-05 15:28:07       41 阅读
  3. RabbitMQ避免重复消费

    2023-12-05 15:28:07       58 阅读
  4. 简谈PostgreSQL的wal_level=logic

    2023-12-05 15:28:07       32 阅读
  5. FlinkSql-Temporal Joins-Lookup Join

    2023-12-05 15:28:07       47 阅读
  6. postgresql树状结构查询示例

    2023-12-05 15:28:07       41 阅读
  7. 高级软件工程15本书籍

    2023-12-05 15:28:07       38 阅读
  8. flask 请求勾子实现 request_auth认证

    2023-12-05 15:28:07       37 阅读
  9. 第九章 Flask

    2023-12-05 15:28:07       39 阅读