springboot配置多数据源后mybatis拦截器失效(上)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: springboot配置多数据源后mybatis拦截器失效
关键字:springcloud、mybatis、多数据源负载均衡、拦截器动态分页


配置文件是通过springcloudconfig远程分布式配置。采用阿里Druid数据源。并支持一主多从的读写分离。分页组件通过拦截器拦截带有page后缀的方法名,动态的设置total总数。


1. 解析配置文件初始化数据源


@Configuration
public class DataSourceConfiguration {
    /**
     * 数据源类型
     */
    @Value("${spring.datasource.type}")
    private Class<? extends DataSource> dataSourceType;
    /**
     * 主数据源配置
     *
     * @return
     */
    @Bean(name = "masterDataSource", destroyMethod = "close")
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource masterDataSource() {
        DataSource source = DataSourceBuilder.create().type(dataSourceType).build();
        return source;
    }
    /**
     * 从数据源配置
     *
     * @return
     */
    @Bean(name = "slaveDataSource0")
    @ConfigurationProperties(prefix = "spring.slave0")
    public DataSource slaveDataSource0() {
        DataSource source = DataSourceBuilder.create().type(dataSourceType).build();
        return source;
    }
    /**
     * 从数据源集合
     *
     * @return
     */
    @Bean(name = "slaveDataSources")
    public List<DataSource> slaveDataSources() {
        List<DataSource> slaveDataSources = new ArrayList();
        slaveDataSources.add(slaveDataSource0());
        return slaveDataSources;
    }
}


2. 定义数据源枚举类型


public enum DataSourceType {
    master("master", "master"), slave("slave", "slave");
    private String type;
    private String name;
    DataSourceType(String type, String name) {
        this.type = type;
        this.name = name;
    }
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}


3. TheadLocal保存数据源类型


public class DataSourceContextHolder {
    private static final ThreadLocal<String> local = new ThreadLocal<String>();
    public static ThreadLocal<String> getLocal() {
        return local;
    }
    public static void slave() {
        local.set(DataSourceType.slave.getType());
    }
    public static void master() {
        local.set(DataSourceType.master.getType());
    }
    public static String getJdbcType() {
        return local.get();
    }
    public static void clearDataSource(){
        local.remove();
    }
}


4. 自定义sqlSessionProxy,并将数据源填充到DataSourceRoute


@Configuration
@ConditionalOnClass({EnableTransactionManagement.class})
@Import({DataSourceConfiguration.class})
public class DataSourceSqlSessionFactory {
    private Logger logger = Logger.getLogger(DataSourceSqlSessionFactory.class);
    @Value("${spring.datasource.type}")
    private Class<? extends DataSource> dataSourceType;
    @Value("${mybatis.mapper-locations}")
    private String mapperLocations;
    @Value("${mybatis.type-aliases-package}")
    private String aliasesPackage;
    @Value("${slave.datasource.number}")
    private int dataSourceNumber;
    @Resource(name = "masterDataSource")
    private DataSource masterDataSource;
    @Resource(name = "slaveDataSources")
    private List<DataSource> slaveDataSources;
    @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        logger.info("======================= init sqlSessionFactory");
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(roundRobinDataSourceProxy());
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        sqlSessionFactoryBean.setMapperLocations(resolver.getResources(mapperLocations));
        sqlSessionFactoryBean.setTypeAliasesPackage(aliasesPackage);
        sqlSessionFactoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);
        return sqlSessionFactoryBean.getObject();
    }
    @Bean(name = "roundRobinDataSourceProxy")
    public AbstractRoutingDataSource roundRobinDataSourceProxy() {
        logger.info("======================= init robinDataSourceProxy");
        DataSourceRoute proxy = new DataSourceRoute(dataSourceNumber);
        Map<Object, Object> targetDataSources = new HashMap();
        targetDataSources.put(DataSourceType.master.getType(), masterDataSource);
        if(null != slaveDataSources) {
            for(int i=0; i<slaveDataSources.size(); i++){
                targetDataSources.put(i, slaveDataSources.get(i));
            }
        }
        proxy.setDefaultTargetDataSource(masterDataSource);
        proxy.setTargetDataSources(targetDataSources);
        return proxy;
    }
}


5. 自定义路由


public class DataSourceRoute extends AbstractRoutingDataSource {
    private Logger logger = Logger.getLogger(DataSourceRoute.class);
    private final int dataSourceNumber;
    public DataSourceRoute(int dataSourceNumber) {
        this.dataSourceNumber = dataSourceNumber;
    }
    @Override
    protected Object determineCurrentLookupKey() {
        String typeKey = DataSourceContextHolder.getJdbcType();
        logger.info("==================== swtich dataSource:" + typeKey);
        if (typeKey.equals(DataSourceType.master.getType())) {
            return DataSourceType.master.getType();
        }else{
            //从数据源随机分配
            Random random = new Random();
            int slaveDsIndex = random.nextInt(dataSourceNumber);
            return slaveDsIndex;
        }
    }
}


6. 定义切面,dao层定义切面


@Aspect
@Component
public class DataSourceAop {
    private Logger logger = Logger.getLogger(DataSourceAop.class);
    @Before("execution(* com.dbq.iot.mapper..*.get*(..)) || execution(* com.dbq.iot.mapper..*.isExist*(..)) " +
            "|| execution(* com.dbq.iot.mapper..*.select*(..)) || execution(* com.dbq.iot.mapper..*.count*(..)) " +
            "|| execution(* com.dbq.iot.mapper..*.list*(..)) || execution(* com.dbq.iot.mapper..*.query*(..))" +
            "|| execution(* com.dbq.iot.mapper..*.find*(..))|| execution(* com.dbq.iot.mapper..*.search*(..))")
    public void setSlaveDataSourceType(JoinPoint joinPoint) {
        DataSourceContextHolder.slave();
        logger.info("=========slave, method:" + joinPoint.getSignature().getName());
    }
    @Before("execution(* com.dbq.iot.mapper..*.add*(..)) || execution(* com.dbq.iot.mapper..*.del*(..))" +
            "||execution(* com.dbq.iot.mapper..*.upDate*(..)) || execution(* com.dbq.iot.mapper..*.insert*(..))" +
            "||execution(* com.dbq.iot.mapper..*.create*(..)) || execution(* com.dbq.iot.mapper..*.update*(..))" +
            "||execution(* com.dbq.iot.mapper..*.delete*(..)) || execution(* com.dbq.iot.mapper..*.remove*(..))" +
            "||execution(* com.dbq.iot.mapper..*.save*(..)) || execution(* com.dbq.iot.mapper..*.relieve*(..))" +
            "|| execution(* com.dbq.iot.mapper..*.edit*(..))")
    public void setMasterDataSourceType(JoinPoint joinPoint) {
        DataSourceContextHolder.master();
        logger.info("=========master, method:" + joinPoint.getSignature().getName());
    }
}


7. 最后在写库增加事务管理


@Configuration
@Import({DataSourceConfiguration.class})
public class DataSouceTranscation extends DataSourceTransactionManagerAutoConfiguration {
    private Logger logger = Logger.getLogger(DataSouceTranscation.class);
    @Resource(name = "masterDataSource")
    private DataSource masterDataSource;
    /**
     * 配置事务管理器
     *
     * @return
     */
    @Bean(name = "transactionManager")
    public DataSourceTransactionManager transactionManagers() {
        logger.info("===================== init transactionManager");
        return new DataSourceTransactionManager(masterDataSource);
    }
}


8. 在配置文件中增加数据源配置


spring.datasource.name=writedb
spring.datasource.url=jdbc:mysql://192.168.0.1/master?useUnicode=true&amp;characterEncoding=utf8&amp;autoReconnect=true&amp;failOverReadOnly=false
spring.datasource.username=root
spring.datasource.password=1234
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.filters=stat
spring.datasource.initialSize=20
spring.datasource.minIdle=20
spring.datasource.maxActive=200
spring.datasource.maxWait=60000
#从库的数量
slave.datasource.number=1
spring.slave0.name=readdb
spring.slave0.url=jdbc:mysql://192.168.0.2/slave?useUnicode=true&amp;characterEncoding=utf8&amp;autoReconnect=true&amp;failOverReadOnly=false
spring.slave0.username=root
spring.slave0.password=1234
spring.slave0.type=com.alibaba.druid.pool.DruidDataSource
spring.slave0.driver-class-name=com.mysql.jdbc.Driver
spring.slave0.filters=stat
spring.slave0.initialSize=20
spring.slave0.minIdle=20
spring.slave0.maxActive=200
spring.slave0.maxWait=60000


相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
2月前
|
Java 开发者 微服务
手写模拟Spring Boot自动配置功能
【11月更文挑战第19天】随着微服务架构的兴起,Spring Boot作为一种快速开发框架,因其简化了Spring应用的初始搭建和开发过程,受到了广大开发者的青睐。自动配置作为Spring Boot的核心特性之一,大大减少了手动配置的工作量,提高了开发效率。
69 0
|
12天前
|
Java Maven Spring
SpringBoot配置跨模块扫描问题解决方案
在分布式项目中,使用Maven进行多模块开发时,某些模块(如xxx-common)没有启动类。如何将这些模块中的类注册为Spring管理的Bean对象?本文通过案例分析,介绍了两种解决方案:常规方案是通过`@SpringBootApplication(scanBasePackages)`指定扫描路径;推荐方案是保持各模块包结构一致(如com.xxx),利用SpringBoot默认扫描规则自动识别其他模块中的组件,简化配置。
SpringBoot配置跨模块扫描问题解决方案
|
19天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
70 14
|
2月前
|
缓存 IDE Java
SpringBoot入门(7)- 配置热部署devtools工具
SpringBoot入门(7)- 配置热部署devtools工具
58 1
SpringBoot入门(7)- 配置热部署devtools工具
|
2月前
|
存储 前端开发 JavaScript
springboot中路径默认配置与重定向/转发所存在的域对象
Spring Boot 提供了简便的路径默认配置和强大的重定向/转发机制,通过合理使用这些功能,可以实现灵活的请求处理和数据传递。理解并掌握不同域对象的生命周期和使用场景,是构建高效、健壮 Web 应用的关键。通过上述详细介绍和示例,相信读者能够更好地应用这些知识,优化自己的 Spring Boot 应用。
40 3
|
2月前
|
Java 数据库连接
SpringBoot配置多数据源实战
第四届光学与机器视觉国际学术会议(ICOMV 2025) 2025 4th International Conference on Optics and Machine Vision
69 8
|
2月前
|
Java 数据库连接 数据库
springboot启动配置文件-bootstrap.yml常用基本配置
以上是一些常用的基本配置项,在实际应用中可能会根据需求有所变化。通过合理配置 `bootstrap.yml`文件,可以确保应用程序在启动阶段加载正确的配置,并顺利启动运行。
268 2
|
3月前
|
Java 数据库连接 Maven
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和MyBatis Generator,使用逆向工程来自动生成Java代码,包括实体类、Mapper文件和Example文件,以提高开发效率。
163 2
mybatis使用一:springboot整合mybatis、mybatis generator,使用逆向工程生成java代码。
|
3月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
92 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
3月前
|
前端开发 Java Apache
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
本文详细讲解了如何整合Apache Shiro与Spring Boot项目,包括数据库准备、项目配置、实体类、Mapper、Service、Controller的创建和配置,以及Shiro的配置和使用。
638 1
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个