背景

我们的系统是使用的tk.mybatis,原来线上的一个表用的uuid主键。来了一个业务需求,需要新增一个自增的索引列(ALTER TABLEtestADD COLUMNuniq_indexBIGINT AUTO_INCREMENT UNIQUE COMMENT '自增索引';

增加了这个列之后,调用insert(object)插入方法,会将传入的对象的id回显(object.id=自增id);

然而,我们期望的是回显主键uuid(线上逻辑),但是实际上回显的是自增id,不符合需求,会导致bug。

参考作者的文档

https://github.com/mybatis-book/book/issues/45

单步调试

从insert开始一步一步地往里走

mybatis 主键索引_mybatis

mybatis 主键索引_优先级_02

应该就是这个后置处理的时候会回写id

mybatis 主键索引_优先级_03


回头看发现是这里产生的generatedkey

mybatis 主键索引_mybatis_04

通过参考作者写的文档:https://github.com/abel533/Mapper/wiki/2.3-generatedvalue

我们发现生成的key有一个优先级顺序

我们原先是用的是一个类来实现,是最低优先级的。

mybatis 主键索引_mybatis 主键索引_05


使用这种key生成方法,会在我们不传入key的情况下,自动用我们指定的类来生成id。

但是,这种方法,会导致我们在有一个自增id的列的情况下,会把自增id填充到我们insert传入的对象的id,这个其实是不符合预期的,我们还是希望返回的id是uuid的id。为了解决这个问题,参考上面的文档,可以做下面的注解,使用sql语句在插入前生成一个uuid,然后作为key插入。这样就可以解决上述的问题。

mybatis 主键索引_mybatis 主键索引_06

mybatis 主键索引_自增_07

使用新的配置之后,会在prepareStatement的时候,利用我们配置sql查询到uuid,作为id用于插入数据库

mybatis 主键索引_自增_08

然后进入一个好深的调用栈,最终调用我们定义的sql语句获取id

mybatis 主键索引_mybatis_09

如果是原来的配置为啥就会把id给改了,是哪里导致代码执行逻辑不一致呢?

我们把配置改回原来的样子:使用自定义的类生成id

mybatis 主键索引_自增_10

再单步调试走一遍

发现keyGenerator不是一样的:

mybatis 主键索引_github_11

找到凶手!

mybatis 主键索引_自增_12


当我们走这个配置genId=xxx.class的时候,keyGenerator是 Jdbc3KeyGenerator 这个在processBefore函数里啥也不干;在processAfter函数里会调用populateKeys,将id给覆盖掉!当我们走配置@KeySql(sql = "select REPLACE(uuid(),'-','')", order = ORDER.BEFORE) 的时候, keyGenerator是selectKeyGenerator,而他的processAfter函数就不会有populateKeys这种逻辑,所以就不会把id覆盖掉了~

mybatis 主键索引_mybatis_13

mybatis 主键索引_mybatis_14