首先說一下背景,目前筆者的工作是物聯網方面的,裝置有對應的智慧運營平臺,平臺開發中建表的主鍵用的是Mybatis plus預設的雪花演算法來生成的,也就是分散式系統比較常用的雪花ID,技術棧就是常用的Spring boot+Spring could Alibaba,json工具用的是FastJson。
在開發的過程中遇到了一個問題:前端接收到的資料在回傳給後端的時候ID總是不對,仔細排查發現,前端接收到的資料的ID末尾兩到三位數字都變成了0。雪花ID的長度是19位數字,系統在bean中的ID用的是Long型別,資料庫建表用的是bigint,接收雪花ID自然沒有問題,但是前端的number型別只能接收16位數字,準確的說是:2的53次方減1,即為9007199254740991,所以回傳的ID不對是數字精度丟失的原因造成的。
知道了原因,解決方案也很簡單,後端傳給前端時把ID轉換位字串型別,前端接收字串就不會丟失精度了,前端把ID回傳給後端的時候,Spring的反序列化會自動為我們轉成Long型別,這麼一來就解決問題了。針對這一思路,樓主想到了兩種解決方案。
1、@JsonSerialize註解
JsonSerialize註解可以幫我們實現欄位值的序列化和反序列話,@JsonSerialize(using = ToStringSerializer.class),程式碼如下:
public class Device{ @ApiModelProperty(value = "物聯終端id") @TableId(type = IdType.ASSIGN_ID) @JsonSerialize(using = ToStringSerializer.class) private Long deviceId; ... }
在需要解決數字過長的欄位上新增sonSerialize註解就可以完美解決這一問題,但是開發的時候一定要注意,萬一漏掉很容易踩坑,所以在員工培訓的時候一定要有所交待。
2、過濾器
過濾器是一種一勞永逸的方法,筆者的專案引入的是fastjson依賴,fastjson可以通過SerializeFilters定製序列化,非常方便,先上程式碼:
package com.johanChan.app.config; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.serializer.ValueFilter; import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; import org.apache.commons.lang3.StringUtils; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.HttpMessageConverter; import java.util.ArrayList; import java.util.List; /** * @author JohanChan * @ProjectName Demo * @Description 與前端互動時對實體類中Long型別的ID欄位序列號 * @time 2021/6/23 11:30 */ @Configuration public class CustomFastJsonHttpMessageConverter { @Bean public HttpMessageConverters fastJsonHttpMessageConverters() { FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter(); FastJsonConfig fastJsonConfig = new FastJsonConfig(); List<SerializerFeature> list = new ArrayList<>(); list.add(SerializerFeature.PrettyFormat); list.add(SerializerFeature.WriteMapNullValue); list.add(SerializerFeature.WriteNullStringAsEmpty); list.add(SerializerFeature.WriteNullListAsEmpty); list.add(SerializerFeature.QuoteFieldNames); list.add(SerializerFeature.WriteDateUseDateFormat); list.add(SerializerFeature.DisableCircularReferenceDetect); list.add(SerializerFeature.WriteBigDecimalAsPlain); fastJsonConfig.setSerializerFeatures(list.toArray(new SerializerFeature[list.size()])); fastConverter.setFastJsonConfig(fastJsonConfig); HttpMessageConverter<?> converter = fastConverter; fastJsonConfig.setSerializeFilters(new ValueFilter() { @Override public Object process(Object object, String name, Object value) { /*if ((StringUtils.endsWith(name, "Id") || StringUtils.equals(name,"id")) && value != null && value.getClass() == Long.class) {*/ if (value != null && value.getClass() == Long.class ) { Long v = (Long) value; if (v.toString().length() > 15) { return String.valueOf(value); } } return value; } }); return new HttpMessageConverters(converter); } }
在ValueFilter中自定義規則,long型別的變數值如果超過15位數則轉化成字串,前端的number型別可以接收16位數字,為什麼不用16位判斷呢?前面已經說過,前端雖然可以接收16位的數字,但最大是9007199254740991,如果用16位做判斷,就會有漏網之魚了。這種方法省心省力,基本上開發人員不需要再注意這種數字過大的問題,但是使用的時候也要有所考量,根據實際業務考慮系統中有沒有其他需求需要用較長的數字,統一用過濾器會不會受到影響。