mybatisplus savebatch 多数据源时候,sqlSessionFactory 不正确踩坑记录。
创始人
2024-02-25 14:18:46
0

记录一下 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 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 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 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 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"));

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

相关内容

热门资讯

制度深耕 丰景如峰 大豆收获作业。庞遵明摄 □本报记者 姜斌 刘畅 银装素裹的北大荒,是一卷由冰雪、沃土与数据共同谱写的...
每经热评|告慰小洛熙,唯有权威... 每经评论员 付克友 宁波5月龄女婴“小洛熙”在医院接受心脏手术后不幸离世,连日来引发舆论关注。一个尚...
从同仁堂涉假等案件谈:岂能将法... 我们生产网络舆情和危机管理专业有用的观点! 文/燕博士 临近年末,出现了两种类型的舆情。 一是,一些...
新疆乌苏银发调解员专解邻里纠纷 11月30日,新疆维吾尔自治区乌苏市寒意渐浓,乌苏市公安局虹桥街道派出所“夕阳红”调解室里却暖意融融...
瑞丽通报消费者购买翡翠后产生纠... 央广网德宏12月21日消息(记者 魏文青)12月19日,有顾客在瑞丽多宝之城之城购买翡翠后产生纠纷,...
杭州靠谱离婚律师推荐:程明律师... 在杭州,当人们面临离婚这一人生重大抉择时,寻找一位靠谱的离婚律师至关重要。离婚不仅涉及情感的纠葛,更...
刷到“自己”直播卖货?拆解“A... 刷到“自己”正在直播卖货?AI“分身”已悄然上线伪造签名、授权书,编造“联名款”一套造假流程行云流水...
靠谱的房产律师怎么收费及专业房... 在房产交易、继承、婚姻等诸多涉及房产权益的事务中,靠谱的房产律师显得尤为重要。那么,如何找到靠谱的房...
公安机关成功侦办一起美方通报的... 日前,辽宁省沈阳市公安局成功侦破“佟某某等人非法经营案”。 2024年4月,一条美方通报的中国籍人员...
学习贯彻党的二十届四中全会精神... 本报讯 (记者庞慧敏)“工资必须按月足额支付给农民工,任何单位和个人都不得拖欠。”这是广西壮族自治区...