spring中动态数据源切换失效问题剖析

我们在使用spring中动态数据源的时候,往往在业务代码中需要指定数据源,在进行业务操作的时候就会去指定的数据源操作数据:代码如下

数据源,两个数据源,ds1,ds2:

@Bean
    public DataSource dynamicDataSource() {

        Map<Object, Object> targetDataSources = new HashMap<>();
        ComboPooledDataSource ds1 = getDs1();
        targetDataSources.put("ds1", ds1);
        targetDataSources.put("ds2", getDs2());
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(ds1);
        return dynamicDataSource;
    }

业务代码切换数据源代码如下:

    @TargetSource("ds1")
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void transation(ConsultConfigArea area, ZgGoods zgGoods) {
        areaService.addArea(area);
        goodsService.addGoods(zgGoods);
    }

    @TargetSource("ds2")
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public int addArea(ConsultConfigArea area) {
        int i = commonMapper.addArea(area);
        return i;
    }


    @TargetSource("ds2")
    @Transactional
    @Override
    public void addGoods(ZgGoods zgGoods) {
        int i = commonMapper.addGood(zgGoods);
    }

我们的意图是,areaService.addArea(area);需要切换到ds2数据源。

测试结论:areaService.addArea(area);还是使用的ds1数据源连接对象,也就是前面那个事务的数据源连接对象。也就是说@TargetSource("ds2")注解失效了。

为什么会这样呢?

因为spring的事务传播属性导致的,前面那个  transation 方法已经有事务了,而transation方法下面的addArea方法事务传播属性是  propagation = Propagation.REQUIRED  这个传播属性就决定了addArea方法还是使用的 transation 方法的数据源连接对象,所以当执行到addArea的时候根本就不存在创建新的连接对象的可能

由于addArea采用的是 Propagation.REQUIRED传播属性,就不存在事务的挂起和doBegin操作,还是沿用的前面方法的数据库连接对象,所以这时候是没有从数据库连接池里面拿连接的。ok,看看doBegin方法:

只有走到doBegin方法时,spring才会从连接池对象中拿连接对象,而现在是没走到doBegin方法中来,所以addArea方法还是沿用的之前的连接对象,所以这里的@TargetSource("ds2")注解其实是没起作用的。

OK,希望得到帮助的同学,点个赞,谢谢

史上最深最全spring源码教程 - 网易云课堂

### MPJ 插件在多数据源环境下的失效原因分析 在多数据源环境中,`mybatis-plus-join`(简称 MPJ)插件可能会因为 MyBatis 的 `MapperScannerConfigurer` 或者动态数据源配置不当而导致功能失效。具体来说,当多个数据源共存时,如果未正确区分各个数据源对应的 Mapper 文件路径,则可能导致 SQL 执行异常或者批量操作失败。 #### 问题根源 MPJ 是基于 MyBatis Plus 构建的扩展工具,在其内部逻辑中依赖于 MyBatis 的核心机制来解析和执行 SQL。然而,在多数据源场景下,如果没有针对每个数据源单独设置 Mapper XML 文件的位置,MyBatis 可能无法正确定位到正确的 Mapper 配置文件[^3]。这种情况下,即使 MPJ 插件本身无误,也可能因基础框架配置错误而引发问题。 --- ### 解决方案 以下是解决 MPJ 插件在多数据源环境下失效的具体方法: #### 1. 动态指定 Mapper XML 路径 为了防止不同数据源之间的 Mapper 文件冲突,需为每个数据源显式定义专属的 Mapper XML 存储目录。可以通过如下代码实现资源路径分离: ```java @Resource private ResourcePatternResolver resolver; @Bean public ConfigurationCustomizer configurationCustomizer() { return configuration -> { try { // 数据源A的Mapper路径 String dataSourceAMapperPath = "classpath:mapper/datasource-a/*.xml"; Resource[] resourcesForDataSourceA = resolver.getResources(dataSourceAMapperPath); // 数据源B的Mapper路径 String dataSourceBMapperPath = "classpath:mapper/datasource-b/*.xml"; Resource[] resourcesForDataSourceB = resolver.getResources(dataSourceBMapperPath); // 将两个数据源的Mapper文件注册至MyBatis配置中 List<Resource> allResources = Stream.concat(Arrays.stream(resourcesForDataSourceA), Arrays.stream(resourcesForDataSourceB)) .collect(Collectors.toList()); configuration.setMapperLocations(allResources.toArray(new Resource[0])); } catch (IOException e) { throw new RuntimeException("Failed to load mapper files", e); } }; } ``` 上述代码通过 `ResourcePatternResolver` 明确指定了各数据源对应 Mapper 文件所在的路径,并将其加载到 MyBatis 的全局配置中。 --- #### 2. 使用独立的 SqlSessionFactory 实例 对于复杂的多数据源架构,建议分别为每个数据源创建独立的 `SqlSessionFactory` 和 `TransactionManager` 实例。这样可以有效隔离不同数据源间的事务管理与映射关系。 以下是一个典型的 Spring Boot 配置示例: ```java @Configuration @MapperScan(basePackages = "com.example.mapper.datasourcea", sqlSessionFactoryRef = "dataSourceASqlSessionFactory") public class DataSourceAConfig { @Bean(name = "dataSourceA") @ConfigurationProperties(prefix = "spring.datasource.a") public DataSource primaryDataSource() { return DataSourceBuilder.create().build(); } @Bean(name = "dataSourceASqlSessionFactory") public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSourceA") DataSource dataSource) throws Exception { SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); // 加载特定的数据源Mapper文件 ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); sessionFactory.setMapperLocations(resolver.getResources("classpath:mapper/datasource-a/*.xml")); return sessionFactory.getObject(); } } // 同理可为其他数据源提供类似的配置类... ``` 此配置确保了每套数据源拥有自己的一组 Mapper 文件集合,从而避免相互干扰。 --- #### 3. 确保 MPJ 插件正常初始化 由于 MPJ 基于 Lombok 提供的功能增强,因此需要确认项目已引入必要的依赖项并完成相关初始化工作。通常只需在项目的根 POM 中加入以下内容即可满足需求: ```xml <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>最新版本号</version> <scope>provided</scope> </dependency> <!-- MPJ Plugin --> <dependency> <groupId>cn.hutool</groupId> <artifactId>mybatis-plus-join</artifactId> <version>最新版本号</version> </dependency> ``` 此外还需验证是否存在自定义拦截器覆盖默认行为的情况;若有,请仔细检查这些拦截器是否兼容 MPJ 特性[^1]。 --- ### 总结 综上所述,要使 MPJ 插件能够在多数据源条件下稳定运行,关键是做好以下几个方面的工作: - **合理划分 Mapper 文件存储区域**; - **构建适配多数据源结构的 SqlSessionFactory 工厂实例**; - **保障所有必要组件均已正确安装部署到位**。 只有做到以上几点才能从根本上杜绝由配置失误引起的各类隐患。 ---
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值