sqlhelper集成dynamic多数据源的分页问题(非教学向)

一.问题描述

最近接手(顶锅)了公司的框架维护工作,第一项任务就是集成dynamic多数据源框架。(dynamic官方使用文档,本文不是教学,有兴趣的小伙伴可以自己查阅文档)。集成dynamic之后,一切都很顺利,但是测试到SQLHelper框架的分页功能,出错了:SQLHelper分页功能,全部是按照dynamic指定的primary数据源来处理分页sql的。比如我配置了mysql和oracle两个数据源,并且指定mysql为primary主数据源,然后不管使用哪个数据源进行查询,SQLHelper都是按照primary指定的主数据源mysql进行分页处理,导致用oracle数据源时分页语句sql报错。

二.解决思路

1.找到分页sql的处理入口

通过断点调试,找到了SQLHelper的com.jn.sqlhelper.dialect.internal.Dialect类和处理语句有关(实际上Dialect是一个接口,抽象子类AbstractDialect的属性LimitHandler才是实际sql的逻辑处理类,processSql是处理方法入口。为了方便理解,就以Dialect概括,一个Dialect对应一种数据源的处理)
在这里插入图片描述

2.Dialect和databaseId有关

databaseId其实就是数据源类型,Dialect是处理sql的类,每一种数据源对应一个Dialect,通过databaseId获取对应的Dialect然后进行sql加工处理,com.jn.sqlhelper.mybatis.plugins.pagination的PaginationHandler类beginIfSupportsLimit方法:
在这里插入图片描述

3.实际出问题的地方

其实就是databaseId的获取,com.jn.sqlhelper.mybatis包下MybatisUtils类getDatabaseId方法:

在这里插入图片描述

红框才是罪魁祸首(暂且先不看蓝框),通过断点调试,发现获取的是org.apache.ibatis.session.Configuration类的databaseId。那问题来了,Configuration类的databaseId是什么时候设置的呢?通过Configuration的setDatabaseId方法的断点调试,发现该方法是项目启动的时候才调用。org.mybatis.spring.SqlSessionFactoryBean类的buildSqlSessionFactory方法
在这里插入图片描述

这也解释通了为什么SQLHelper只按照某种数据源来处理分页语句

4.为什么按照primary指定的主数据源处理?

一切从简,上关键代码(接Configuration的setDatabaseId方法):
com.baomidou.dynamic.datasource.DynamicRoutingDataSource类的determineDataSource方法
在这里插入图片描述
com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder类的peek()方法返回当前线程/代码使用的数据源名称,启动时没有触发push操作,所以返回为空。
在这里插入图片描述
空值则返回primary指定的默认数据库,所以Configuration的databaseId是dynamic在配置文件用primary指定的主数据源:
在这里插入图片描述
5.DynamicDataSourceContextHolder的push:
dynamic会将当前线程使用的数据源存入threadlocal里
com.baomidou.dynamic.datasource.aop.DynamicDataSourceAnnotationInterceptor.invoke方法,会将当前线程使用的数据源名称存入
在这里插入图片描述
这个push触发的机制,应该是dynamic注解扫描到的地方。这也解释了为什么启动的时候peek返回空,因为没有触发push操作。

三.解决方案:

既然知道问题所在,那么就有解决思路了:MybatisUtils的getDatabaseId方法,返回正确的数据就行。

1.不走全局获取databaseId

如果能提前知道现在的数据源是什么,然后通过这个数据源获取databaseId就好了。巧的是DynamicDataSourceContextHolder类的peek()是一个静态方法,并且入参MappedStatement ms可以获取到dynamic配置的所有数据源信息:
在这里插入图片描述

2.通过connection获取databaseId

其实就是二.3的蓝框放前面就行,由于集成了连接池,所以不需要担心查询之前多连接一次数据库。tx.getConnection()源码有兴趣的也可以翻一下哦。

3.后续版本优化

四.小结

  1. SQLHelper根据databaseId(数据源类型)处理分页语句的类是LimitHandler;databaseId和LimitHandler、Dialect,是1:1:1的关系。
  2. springboot+tx.mybatis+dynamic+SQLHelper的技术选型下,SQLHelper在dynamic情况下分页处理有问题。
  3. 处理方案经实践,mybatis是完全可以适配的。不知道其他orm框架是否适用!

五.彩蛋时间

最近终于稍微脱离了CRDU的业务代码,开始往底层摸索了。框架需要适配dynamic,其实我也是什么都不懂,没办法只能查资料看看dynamic是什么,然后通过断点调试的方式探索开源框架的处理流程,说实话,最后发现问题并且解决问题的时候,还是有一点自豪感和成就感的!
框架这个版本的需求功能,需要用到的技术栈其实我基本没听过,但是有压力就有动力,能让自己多学一些总是好的!
另外附上和SQLHelper作者在github上的讨论地址:此次论剑,略输一筹(滑稽,HSPCode就是本萌新)

萌新发言,不喜勿喷,欢迎大佬指出不当之处!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值