雪花算法ID在前端丟失精度解決方案


  首先說一下背景,目前筆者的工作是物聯網方面的,設備有對應的智慧運營平台,平台開發中建表的主鍵用的是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位做判斷,就會有漏網之魚了。這種方法省心省力,基本上開發人員不需要再注意這種數字過大的問題,但是使用的時候也要有所考量,根據實際業務考慮系統中有沒有其他需求需要用較長的數字,統一用過濾器會不會受到影響。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM