记录一次在mybatis-plus @TableId注解上踩的坑以及脱坑过程

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


一、 前言

  事情是这样的,项目本来好好的,某一天公司引入了代码安全性扫描,扫出了一个${}符号,我是用在动态表名上,存储数据根据当前年份分表存储,代码控的死死的,绝对不会出现sql注入的情况,但领导不管一定要我改掉。无奈之下,我寻思着mybatis-plus可以平替mybatis,而mybatis-plus里的DynamicTableNameParser可以解决动态表名的问题,于是动手改了,专注点在动态表名上,改完一跑,出事了,原本表自增的主键才几千,一下成了19位的长度的数字,多试了几次,id还是19位,而且还不连续了。网了查了一番,定位到是实体类的id字段没加上@TableId注解。
  我虽然做过多年的开发,但早年用的是hibernate,后面用mybatis,对mybatis-plus的了解,仅局限于了解,只知道它是在mybatis上做的二次开发,是对mybatis的增强,从没真正在项目上用过,所以想当然的以为主键字段不用特殊处理,mybatis-plus拼成的sql执行后会是mysql里的主键自增的值。
  以上就是这次踩的坑,下面就从mybatis-plus的源码里找出这个坑。

二、调试过程

1、搭建测试项目

搭建一个标准的springboot,springMVC,mybatis-plus项目,mybatis-plus的版本是3.4.0,controller功能是往user表里插入数据,项目结构如下:
在这里插入图片描述

2、粗略调试一下,看看id的赋值

实体类如下
在这里插入图片描述
controller里的调试过程,可以看到此时的id是null
在这里插入图片描述进到IService里,id为null,但这里继续下去,直到结束,我会发现全是调用动态代理的代码,不知道是不是我的idea版本问题(2018.3),隐藏了挺多细节,就突然某一步给id赋值了,找不到赋值过程

在这里插入图片描述
在这里插入图片描述重来。。。这里有个技巧,给字段赋值其实就是调实体类里的set方法,在User类里打个断点也许可以抓到一些细节。果然,断点停在@Data这一行时id有值了,debugger列表也是停在setId上,果然!

在这里插入图片描述debugger列表往前看,可以找到一个有用的方法populateKeys,看下细节,这里就是反射调用setId的地方,第131行的identifierGenerator.nextId(entity)就是生成id值的地方了
在这里插入图片描述在131行上打断点,重新发请求,最终在Sequence类里找到了id生成的代码(中间省略了几步),其实就是雪花算法的实现
在这里插入图片描述至此,找到了id赋值的代码了,了解了为什么不配@TableId,mybatis-plus会给id赋一个雪花算法实现的long值了,但是还没满足,为什么不配@TableId,会给我默认搞一个ASSIGN_ID呢,为什么不是mysql里的自增长呢。。。继续调试!

3、找出idType怎么赋值ASSIGN_ID的

上面找到了给id赋值的方法populateKeys,可以看到进入这个方法的时候idType就在tableInfo对象里(第123行),继续往上找调用populateKeys的方法,在同一个类的process方法里
在这里插入图片描述

tableInfo对象的拼装是在第106行,进去看下
在这里插入图片描述
发现tableInfo是在TableInfoHelper类的TABLE_INFO_CACHE里直接获取的(第78行),而TABLE_INFO_CACHE是本类中一个map,就是个缓存,再向上找这个缓存是怎么put值的,在本类中的initTableInfo方法里可以找到put方法
在这里插入图片描述

在initTableInfo方法里(注意这个类里有两个initTableInfo方法,参数不同)打断点以后,发现通过controller是进不来的,说明这个方法是在mybatis-plus初始化时就调用了的,所以这里要重启整个服务才能进入initTableInfo方法。继续,发现是在第126行return,重点看key->initTableInfo(xxxx)
在这里插入图片描述
在第150行执行以后,可以发现tableInfo里有了idType的值,重新启动,跟进initTableFields方法在这里插入图片描述

initTableFields方法挺长,重点看以下我圈出来的,第262行判断User实体类中有没有@TableId注解,结果是false,所以id字段就进入285行initTableIdWithoutAnnotation方法里初始化,跟进去
在这里插入图片描述

注意看第400行,判断出主键就会继续执行if里的代码,idType在415行赋值了,是从dbConfig对象里拿到的(看上一个图的第254行,从globalConfig里拿到)。(DEFAULT_ID_NAME默认名称是id,写其他的它就不认识了,这个方法我个人以为写得不怎么样,就凭一个字段名来判断,要么干脆别判断了,没有@TableId注解就当这个实体类没有主键处理,要么要写好点,去数据库查一下真正的主键字段再来对比,用一个默认的字段名匹配,显得高不高低不低的)
在这里插入图片描述在这里插入图片描述
至此,可以看到,在项目启动时,mybatis-plus就主动识别出了实体类中的主键字段,并且将idType赋值了ASSIGN_ID。不过,我还是没有满足,没有找到ASSIGN_ID怎么来的,接着找吧。

4、找出默认的idType

其实,细心的小伙伴可能已经发现了,在上面的某一步里,globalConfig对象里已经出现过idType=ASSIGN_ID,在TableInfoHelper类里的第142行,就从这里入手,看看能不能找出更多的细节
在这里插入图片描述
在GlobalConfigUtils类的第86行,发现globalConfig是从GLOBAL_CONFIG这个Map中取得的,也是个缓存,找GLOBAL_CONFIG的put方法,不出意料,可以从这个put方法里找到最原始的idType,就在这个类的第72行,setGlobalConfig方法看不出啥,继续向上找调用setGlobalConfig的地方,idea找出了两处调用的地方,但通过Debugger列表可以看出,实际调用setGlobalConfig的是MybatisSqlSessionFactoryBean的buildSqlSessionFactory方法
在这里插入图片描述
MybatisSqlSessionFactoryBean类的第474行setGlobalConfig,而globalConfig里的dbConfig对象是在第471行赋值的,471行的方法指向了这个类GlobalConfig.DbConfig,进去看看
在这里插入图片描述

骚东西在这,在GlobalConfig的内部类DbConfig里!
在这里插入图片描述

三、总结

  经过上面这么多步的调试,终于找出了默认的idType出自哪里,满足了。小小的教训,以后遇到自己不熟悉的技术时,不要凭经验去做事,最起码要先查清楚常规的用法再动手,否则一不小心就掉坑里了 
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值