背景
我們的系統是使用的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覆蓋掉了~