背景
我们的系统是使用的tk.mybatis,原来线上的一个表用的uuid主键。来了一个业务需求,需要新增一个自增的索引列(ALTER TABLE
testADD COLUMN
uniq_indexBIGINT AUTO_INCREMENT UNIQUE COMMENT '自增索引';
)
增加了这个列之后,调用insert(object)插入方法,会将传入的对象的id回显(object.id=自增id);
然而,我们期望的是回显主键uuid(线上逻辑),但是实际上回显的是自增id,不符合需求,会导致bug。
参考作者的文档
https://github.com/mybatis-book/book/issues/45
单步调试
从insert开始一步一步地往里走
应该就是这个后置处理的时候会回写id
回头看发现是这里产生的generatedkey
通过参考作者写的文档:https://github.com/abel533/Mapper/wiki/2.3-generatedvalue
我们发现生成的key有一个优先级顺序
我们原先是用的是一个类来实现,是最低优先级的。
使用这种key生成方法,会在我们不传入key的情况下,自动用我们指定的类来生成id。
但是,这种方法,会导致我们在有一个自增id的列的情况下,会把自增id填充到我们insert传入的对象的id,这个其实是不符合预期的,我们还是希望返回的id是uuid的id。为了解决这个问题,参考上面的文档,可以做下面的注解,使用sql语句在插入前生成一个uuid,然后作为key插入。这样就可以解决上述的问题。
使用新的配置之后,会在prepareStatement的时候,利用我们配置sql查询到uuid,作为id用于插入数据库
然后进入一个好深的调用栈,最终调用我们定义的sql语句获取id
如果是原来的配置为啥就会把id给改了,是哪里导致代码执行逻辑不一致呢?
我们把配置改回原来的样子:使用自定义的类生成id
再单步调试走一遍
发现keyGenerator不是一样的:
找到凶手!
当我们走这个配置genId=xxx.class
的时候,keyGenerator是 Jdbc3KeyGenerator
这个在processBefore函数里啥也不干;在processAfter函数里会调用populateKeys,将id给覆盖掉!当我们走配置@KeySql(sql = "select REPLACE(uuid(),'-','')", order = ORDER.BEFORE)
的时候, keyGenerator是selectKeyGenerator
,而他的processAfter函数就不会有populateKeys这种逻辑,所以就不会把id覆盖掉了~