当前位置:   article > 正文

mybatisplus 多数据源,多个sqlSessionFactory 导致 savebatch 执行不正确_mybatisplus 动态sqlsessionfactory

mybatisplus 动态sqlsessionfactory

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://zhangxiaofan.blog.csdn.net/article/details/128085906

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

具体异常就是 saveBatch 执行的数据源,与期望的不一致。

项目中有2个数据源,1个是单独的数据库A,1个是sharding配置的分表数据源B;

本来是想保存数据到A数据库,结果是执行的sharding的分表数据库B,其实是因为 SqlSessionFactory 错误导致的。

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

第1个 SqlSessionFactory(普通数据库A)

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

第2个 SqlSessionFactory(sharding-sphere数据库B)

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

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

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

注意这里,我用的事务管理器是:myNormalTransactionManager,期望是用第一个 SqlSessionFactory(普通数据库A) ,即myNormalSqlSessionFactory。

但是执行的时候,用的却是 myShardingSqlSessionFactory,第二个数据源(sharding-sphere数据库B)

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

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

经过分析,找下原因:

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 中这行代码改成:(新建了 mapper子目录-normal)

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

将第2个的 SqlSessionFactory 中这行代码改成:(新建了 mapper子目录-sharding)

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/949652
推荐阅读
相关标签
  

闽ICP备14008679号