tk.mybaits动态表名
背景
由于目前项目中多采用模块化的方式进行组件的整合,在新服务的搭建时为了尽可能保证新服务指向的数据库中的表名统一于服务名,比如新服务是订单模块,希望这个模块指向的数据库中的表名都是以ORDER_
开头。且一些二次开发的组件中也有使用mysql
进行数据的相关处理,故希望可以通过统一处理的方式进行表名动态化。
跟踪
tk.mybatis
的大致处理逻辑:
- 扫描器会将所有被
@Mapper
标记的类通过SpringBean
的创建对象流程中,然后创建相应的对象后,添加到SpringBean
容器中。 - 将这个标记的类对象包装为
MapperFactoryBean
对象 - 待到创建
bean
流程的最后,也就是经过了初始化、后置处理器列表等扩展点的相关处理之后,通过包装对象MapperFactoryBean#afterPropertiesSet()
进行tk.mybatis
对象的二次处理。 - 通过这个后置方法,解析出这个
@Mapper
对象所对应的表实体的表名和其他一些在创建SQL
所需要的配置信息。
1 | public class EntityHelper { |
- 根据这个标记对象所继承的上层接口,逐个解析出对应的
SQL
语句。以下方代码为例,这里的上层接口CommonMapper
继承于Mapper
,而Mapper
又继承于很多的上层接口,其中就有selectOne
接口,那么在这一步就会根据第四步骤中解析出来的表信息和字段信息等,构建一个基于变量的SQL
语句,如果业务层调用该方法时,仅将相关参数填充后就形成一条完整的SQL
。
1 |
|
处理方案
根据以上的步骤,可以得出一个结论,如果想要动态(这个动态是指创建时动态,并不是指运行时动态)的修改表名,那么需要在第四步之后,第五步之前将entityTableMap
中对应的表名修改为想要的表名。
根据Springbean
创建对象的逻辑,在初始化后置方法之前,会经过后置处理器列表,所以可以通过模拟一个后置处理器列表,提前对entityTableMap
相关信息进行解析并缓存。
MapperFactoryBean
进行处理的主要逻辑:
根据
XxxMapper
类获取到对应的表实体通过
EntityHelper.initEntityNameMap()
方法解析出这个表实体的相关信息,并缓存。将
XxxMapper.selectExample()
等等内置的通用接口解析为动态SQL语句,缓存在某个地方(这个我没有去找在哪里)
综上:只要能在 第3步之前将EntityHelper
中的这个缓存中的EntityTable
的表名称修改为相应的值,就能实现将表名动态化。
1 | public class TablePrefixBeanPostProcessor implements BeanPostProcessor { |
上方代码中的最后的判断语句可以根据相应的规则,比如说自定义一个注解,并且在注解中指定一个前缀的参数,标记在实体上,这里可以通过获取这个注解进行是否需要添加前缀的逻辑处理。
总结
实际上以上这种场景应该在真是情况中少之又少,一般情况下并不会说想要统一某个服务中的所有表名前缀,但是在解决的问题的过程中,其实对springBean
创建实体的流程,以及tk.mybatis
对实体解析的相关逻辑都大致的过了一遍,加强了框架中一些细节处理的印象,对于后续如果出现一些bug
可以尽快的定位到问题。
实际上我并不是一下子就想到这么做,在想到这么做之前,我也经过了其他的尝试,比方说我最先想到的是通过写mybatis
插件的方式,拦截出BoundSql
具体的SQL
语句,通过修改这个SQL
语句达到目的,但是发现不同类型的SQL
判断方式略有不同,操作难度大,所以一直往上层追溯,发现BoundSql
中的语句是在Bean
初始化过程就已经生成,后面才一点一点整理出上述步骤流程,从而找到下手的地方。