最近公司的一個專案組要把以前的單體應用進行為服務拆分,表的ID主鍵使用Mybatis plus默認 的雪花演算法來生成,
快下班的時候,小伙伴跑過來找我,:“快給我看看這問題,卡這卡了小半天了!”,連拉帶拽,連哄帶騙的把我拉到他的電腦前面,這位小伙伴在我看來技術不算是大牛,但經驗也很豐富了,他都卡了半天的問題,應該不是小問題,如果我一時半會搞不定,真的是耽誤我下班了,所以我很不情愿的在他的位置坐了下來,
一、現象是這樣的
下面我把例外的現象給大家描述一下,小伙伴建了一張表,表的主鍵是id BigINT,用來存盤雪花演算法生成的ID,嗯,這個沒有問題!
CREATE TABLE user
(
id BIGINT(20) NOT NULL COMMENT '主鍵ID',
#其他欄位省略
);
使用Long 型別對應資料庫ID資料,嗯,也沒有問題,雪花演算法生成的就是一串數字,Long型別屬于標準答案!
@Data
public class User {
private Long id;
//其他成員變數省略
在后端下斷點,看到資料回應以JSON回應給前端,正常
{
id:1297873308628307970,
//其他屬性省略
}
最后,這條資料回傳給前端,前端接收到之后,修改這條資料,后端再次接識訓來,奇怪的問題出現了:后端重新接識訓來的id變成了:12978733086283000000,不再是1297873308628307970
二、分析問題
我的第一感覺是,開發小伙伴把資料給搞混了,張冠李戴了,把XXX的物件ID放到了YYY物件的ID上,所以,就按照代碼從前端到后端、從后端到前端除錯跟蹤了一遍,
從代碼的邏輯角度上沒有任何問題,這時,我有點煩躁了,真的是耽誤我下班了!但開工沒有回頭箭,既然坐下來了就得幫他解決,不然以后這隊伍怎么帶?想到這我又靜下心來,開始思考,
1297873308628300000 ---> 1297873308628307970
這兩個數長得還挺像的,似乎是被四舍五入了,此時腦袋里面冒出一個想法,是精度丟失了么?哪里能導致精度丟失?
- 服務端都是Long型別的id,不可能丟失
- 前端是什么型別,JSON字串轉js物件,接收Long型別的是number
上網查了一下Number精度是16位(雪花ID是19位的),So:JS的Number資料型別導致的精度丟失,問題是找到了!
小伙伴投來敬佩的眼光,5分鐘就把這問題發現了,可是發現了有什么用?得解決問題啊!
三、解決問題
開發小伙伴說:那我把所有的資料庫表設計,id欄位由Long型別改成String型別吧,我問他你有多少張表?他說100多張吧,
- 100多張表還有100多個物體類需要改
- 還有各種使用到物體類的Service層要改
- Service等改完Controller層要改
- 關鍵的是String和Long都是常用型別,他還不敢批量替換
小伙伴拿起電話打算訂餐,說今晚的加班是無法避免了,我想了想說:你最好別改,String做ID查詢性能會下降,我再想想!后端A到前端B出現精度丟失,要么改前端,要么改后端,要么…… ,“哎哎,你等等先別訂餐,后端A到前端B你用的什么做的序列化?” 小伙伴告訴我說使用的是Jackson,這就好辦了,Jackson我熟悉啊!
解決思路:后端的ID(Long) ==> Jackson(Long轉String) ==> 前端使用String型別的ID,前端使用js string精度就不會丟失了, 那前端再把String型別的19位數字傳回服務端的時候,可以用Long接收么?當然可以,這是Spring反序列化引數接收默認支持的行為,
最終方案就是:前端用String型別的雪花ID保持精度,后端及資料庫繼續使用Long(BigINT)型別不影響資料庫查詢執行效率,
剩下的問題就是:在Spring Boot應用中,使用Jackson進行JSON序列化的時候怎么將Long型別ID轉成String回應給前端,方案如下:
@Configuration
public class JacksonConfig {
@Bean
@Primary
@ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder)
{
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
// 全域配置序列化回傳 JSON 處理
SimpleModule simpleModule = new SimpleModule();
//JSON Long ==> String
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
objectMapper.registerModule(simpleModule);
return objectMapper;
}
}
小伙伴放下電話, 再次投來敬佩眼光,“走吧,一起下班!”我和小伙伴說,小伙伴一路上一直問我你是怎么學習的?我冠冕堂皇的說了一些多想多學多問之類的話,
其實我心里在想:我是一個懶人,但我不能說,能躺著絕不坐著,能自動絕不手動,能打車絕不自己開車,第一次就把事情做對,才是省時省力做好的方法!這么多年的“懶”,決定了我需要去思考更多的“捷徑”,思考“捷徑”的程序是我不斷進階的訣竅!
勤奮的人是社會的生產力,而懶人是社會的創造力!
歡迎關注我的博客,里面有很多精品合集
- 本文轉載注明出處(必須帶連接,不能只轉文字):字母哥博客,
覺得對您有幫助的話,幫我點贊、分享!您的支持是我不竭的創作動力! ,另外,筆者最近一段時間輸出了如下的精品內容,期待您的關注,
- 《手摸手教你學Spring Boot2.0》
- 《Spring Security-JWT-OAuth2一本通》
- 《實戰前后端分離RBAC權限管理系統》
- 《實戰SpringCloud微服務從青銅到王者》
- 《VUE深入淺出系列》
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/53094.html
標籤:Java
