mybatisplus savebatch 多数据源时候,sqlSessionFactory 不正确踩坑记录。

news2025/1/19 20:40:51

记录一下 mybatis-plus + sharding-JDBC 的时候,因为配置多数据源和多个SqlSessionFactory导致 mybatisPlus 执行 saveBatch 异常的问题。

具体异常就是 saveBatch 执行的数据源,与期望的不一致。其实是因为 SqlSessionFactory 错误导致的。

项目中有2个数据源,分别用的不同的 SqlSessionFactory。

第1个 SqlSessionFactory

@Primary
    @Bean(name = "myNormalSqlSessionFactory")
    public SqlSessionFactory getMybatisSqlSessionFactory(@Qualifier("myNormalDataSource") DataSource myDataSource) throws Exception {
        MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
        bean.setDataSource(myDataSource);
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        // 定义多个 sqlSessionFactory 的时候注意 mapper 要指定子目录,否则会 MybatisPlus 会出现 sqlSessionFactory 不正确。
        // 原因详见: TableInfoHelper.initTableInfo()
        bean.setMapperLocations(resolver.getResources("classpath*:mapper/**/*Mapper.xml"));
        bean.setConfigLocation(new DefaultResourceLoader().getResource("classpath:mybatis-config.xml"));
        List<Interceptor> interceptors = new ArrayList<>();
        interceptors.add(mybatisPlusInterceptor);
        bean.setPlugins(interceptors.toArray(new Interceptor[0]));
        Properties properties = new Properties();
        properties.put("dialect", "mysql");
        bean.setConfigurationProperties(properties);
        return bean.getObject();
    }

第2个 SqlSessionFactory

@Bean(name = "myShardingSqlSessionFactory")
    public SqlSessionFactory getMybatisSqlSessionFactory(@Qualifier("myShardingDataSource") DataSource myDataSource) throws Exception {
        MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(myDataSource);
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        // 定义多个 sqlSessionFactory 的时候注意 mapper 要指定子目录,否则会 MybatisPlus 会出现 sqlSessionFactory 不正确。
        // 原因详见: TableInfoHelper.initTableInfo()
        sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:mapper/**/*Mapper.xml"));
        sqlSessionFactoryBean.setConfigLocation(new DefaultResourceLoader().getResource("classpath:mybatis-config.xml"));
        List<Interceptor> interceptors = new ArrayList<>();
        interceptors.add(mybatisPlusInterceptor);
        sqlSessionFactoryBean.setPlugins(interceptors.toArray(new Interceptor[0]));
        Properties properties = new Properties();
        properties.put("dialect", "mysql");
        sqlSessionFactoryBean.setConfigurationProperties(properties);
        return sqlSessionFactoryBean.getObject();
    }

启动项目后,执行了  saveBatch()

 @GetMapping(value = "/normal/student/insert/batch")
    @Transactional(value = "myNormalTransactionManager", rollbackFor = Exception.class)
    public String test1() {
        List<StudentBase> studentList = StudentUtil.getRandomStudentBaseList(5);
        studentNormalService.saveBatch(studentList);
        return JSON.toJSONString("ok");
    }

注意这里,我用的事务管理器是:myNormalTransactionManager,期望是用第一个 SqlSessionFactory ,即myNormalSqlSessionFactory。

但是执行的时候,用的却是 myShardingSqlSessionFactory (第二个数据源)

我们可以去这类看到 com.baomidou.mybatisplus.extension.toolkit.SqlHelper 

 public static boolean executeBatch(Class<?> entityClass, Log log, Consumer<SqlSession> consumer) {
        SqlSessionFactory sqlSessionFactory = sqlSessionFactory(entityClass);
        SqlSessionHolder sqlSessionHolder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sqlSessionFactory);
        boolean transaction = TransactionSynchronizationManager.isSynchronizationActive();
        if (sqlSessionHolder != null) {
            SqlSession sqlSession = sqlSessionHolder.getSqlSession();
            //原生无法支持执行器切换,当存在批量操作时,会嵌套两个session的,优先commit上一个session
            //按道理来说,这里的值应该一直为false。
            sqlSession.commit(!transaction);
        }
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
        if (!transaction) {
            log.warn("SqlSession [" + sqlSession + "] was not registered for synchronization because DataSource is not transactional");
        }
......

经过分析,找下原因:

com.baomidou.mybatisplus.core.metadata.TableInfoHelper 中 initTableInfo方法会将每个 实体类 与对应的 数据库配置保存到 缓存:TABLE_INFO_CACHE 中

 

也就是。我们在创建 SqlSessionFactory 时候设置的 setMapperLocations, 设置路径下的所有mapper.xml 对应的实体都会保存对应的数据库配置。

因此,我们需要将不同的 SqlSessionFactory 配置,用不同的 mapper 目录来扫描。不同数据源的操作,放在各自的 mapper 子目录下,作区分。


上面我们定义2个SqlSessionFactory中有两句一样的代码:

sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:mapper/**/*Mapper.xml"));

由于 classpath*:mapper/**/*Mapper.xml 路径一样,导致初始化 实体类和数据库配置对应关系,被覆盖的现象。
即同样的 mapper.xml 文件中被 不同的 SqlSessionFactory 扫描了两次,导致mapper.xml中的实体类信息只有一种SqlSessionFactory信息。

将第1个的 SqlSessionFactory 中这行代码改成:

bean.setMapperLocations(resolver.getResources("classpath*:mapper/normal/**/*Mapper.xml"));

将第2个的 SqlSessionFactory 中这行代码改成:

sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:mapper/sharding/**/*Mapper.xml"));

重启项目,执行就正常了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/44206.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

程序员第一次接私活?记住这三点让你事半功倍

不少程序员都有接私活的想法&#xff0c;但恰恰就如开发者之间的论调一样&#xff0c;接私活其实是有一定难度的&#xff0c;想找到合适的单子&#xff0c;顺利地做完并拿到薪水&#xff0c;需要注意的事儿很多&#xff0c;接下来和大家分享一下&#xff0c;程序员第一次接私活…

改进的多目标差分进化算法在电力系统环境经济调度中的应用(Python代码实现)【电气期刊论文复现】

&#x1f389;&#x1f389;&#x1f389;&#x1f389;欢迎您的到来&#x1f60a;&#x1f60a;&#x1f60a; &#x1f96c;博客主页&#xff1a;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 &#x1f4dd;床头铭&#xff1a;将来的我一定会感谢…

[附源码]计算机毕业设计springbootSwitch交流平台

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Android 插件化

demo 如果要加载插件模块编译的apk插件包中的Activity类&#xff0c;需要执行如下流程&#xff1a; 1&#xff09;加载类对象&#xff1a;使用DexClassLoader加载Activity对应的Class字节码类对象&#xff1b; 2&#xff09;管理生命周期&#xff1a;处理加载进来的Activity…

【算法自由之路】前缀树 桶排序之计数排序和基数排序

【算法自由之路】前缀树 & 桶排序之计数排序和基数排序 前缀树&#xff08;字典树&#xff09; 首先是前缀树&#xff0c;前缀树是由字符构成的树结构&#xff0c;它记录有多少前缀字符通过&#xff0c;以及有多少个同样的字符串&#xff0c;其找这类信息的时间复杂度是极…

minigui编译移植

minigui编译移植 一:文件系统依赖支持二:交叉编译libminigui-1.6.10三:交叉编译mg-samples-1.6.10四:资源minigui-res-1.6.10四:开发板拷贝资源五:/etc/MiniGUI.cfg配置文件修改六:系统环境变量设置一:文件系统依赖支持 zlib libpng libjpeg 二:交叉编译libminigui-1.6.10 conf…

第五届安洵杯网络挑战赛WP

Crypto Cry1 crypto签到题&#xff0c;就是先对SHA256的哈希值进行爆破&#xff0c;然后猜数字 用hashcat一条命令秒穿 hashcat --custom-charset1 ?d?l?u -a 3 -m 1400 3075696ea46516c3a0a43930fab5a0f1c68ea4b315dd87a9cd123dac7f20f3a6 ?1?1?1?1GJWVMYlh5ApWLbF…

MySQL源码分析之SQL函数执行

1.MySQL中执行一条SQL的总体流程 一条包含函数的SQL语句&#xff0c;在MySQL中会经过: 客户端发送&#xff0c;服务器连接&#xff0c;语法解析&#xff0c;语句执行的过程。 调试源码&#xff0c;分析函数的具体执行过程&#xff0c;在客户端&#xff0c;执行select to_char…

【数据结构与算法】初识时间空间复杂度

文章目录1.数据结构与算法概念2.时间复杂度3.大O计数法表示时间复杂度4.线性结构与非线性结构1.数据结构与算法概念 &#xff08;1&#xff09;什么是数据结构 数据结构指的是相互之间有一种或者多种特定的关系数据元素集合。数据结构可以分成逻辑结构和物理结构。逻辑结构&a…

全网首发克莱斯勒东南大捷龙jeep道奇DIY数码碟盒增加USB和蓝牙播放音乐功能使用原车接口无损改装

文章目录前言碟盒功能1、设计指标3、外观设计4、PCB设计5、程序设计6、调试7、大捷龙车机尾插接口定义公头东南大捷龙车机白色插头模块与白色插头连接方法8、安装方法9、 使用方法9.1 CD车机按钮功能定义11、 联系我前言 ​ 之前写过四篇关于车机增加音频输入的方法。 1、07宝…

[数据结构] 并查集

并查集相关概念并查集的模拟实现1&#xff09;实现基本框架2&#xff09;实现基础操作findRoot查找元素属于哪个集合Union合并两个集合IsOneSet判断两个元素是否属于同一集合SetSize集合个数相关概念 初始时&#xff0c;每个数据的下标都为-1&#xff0c;表示10棵树&#xff1…

【EDA365电子论坛】RISC-V 能否超越 x86、Arm,成为新一代计算机系统架构?

前言 指令集架构(Instruction Set Architecture&#xff0c;缩写为ISA&#xff09;&#xff0c;是一组指令的集合&#xff0c;指令是指处理器进行操作的最小单元&#xff08;譬如加减乘除操作或者读&#xff0f;写存储器数据&#xff09;。指令集架构&#xff0c;有时简称为“架…

[附源码]SSM计算机毕业设计小超市进销存管理系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【js】日期控件的实现

需求&#xff1a;通过日期控件实现只显示年月 效果如下图&#xff1a; 日期控件使用的是My97DatePicker&#xff1a; 可以从官网下载&#xff1a;http://www.my97.net/&#xff0c;或者&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1KRXSjfqpyguZ67vBrOWM8g 提取码…

Python创建增量目录的代码实例

目录1、需求很简单2、代码3、使用方法1、需求很简单 比如我在做机器学习实验的时候&#xff0c;实验结果的保存路径是’runs/exp’。 这样就会出现一个问题&#xff1a;当我第二次运行程序的时候&#xff0c;如果我忘记更改代码中的路径名或者清除上次实验结果&#xff0c;这…

BurpSuite官方实验室之逻辑漏洞

BurpSuite官方实验室之逻辑漏洞 这是BurpSuit官方的实验室靶场&#xff0c;以下将记录个人逻辑漏洞共11个Lab的通关过程 Web Security Academy: Free Online Training from PortSwigger lab1&#xff1a; Excessive trust in client-side controls 过度信任客户端控件 目…

PyTorch学习笔记-Convolution Layers与Pooling Layers

1. Convolution Layers 由于图像是二维的&#xff0c;因此基本上最常用到的就是二维的卷积类&#xff1a;torch.nn.Conv2d&#xff0c;官方文档&#xff1a;torch.nn.Conv2d。 Conv2d 的主要参数有以下几个&#xff1a; in_channels&#xff1a;输入图像的通道数&#xff0c…

IDEA关于数据库报错SQL dialect is not configured或Unable to resolve table ‘表名‘

目录一、SQL dialect is not configured1.1 报错场景展示1.2 方式一&#xff0c;万能altenter1.3 方式二&#xff0c;在setting中设置二、Unable to resolve table 表名2.1 报错场景展示2.2 方式一&#xff0c;万能altenter2.3 方式二&#xff0c;在setting中设置一、SQL diale…

vscode开发STM32(三)---调试篇

vscode开发STM32&#xff08;三&#xff09;—调试篇 文章目录vscode开发STM32&#xff08;三&#xff09;---调试篇前提条件配置调试配置JLink使用JLinkGDB进行调试配置stlink使用openOCD进行调试完整的launch文件内容前提条件 安装Cortex-Debug插件 安装OpenOCD 安装JLink驱…

LeetCode HOT 100 —— 48.旋转图像

题目 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 思路 方法一&#xff1a;使用辅助数组 可以得出规律&#xff0c;将图像旋…