雪花算法ID在前端丢失精度解决方案

news/2024/7/10 1:51:41 标签: 字符串, java, spring, spring boot, vue

首先说一下背景,目前笔者的工作是物联网方面的,设备有对应的智慧运营平台,平台开发中建表的主键用的是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位做判断,就会有漏网之鱼了。这种方法省心省力,基本上开发人员不需要再注意这种数字过大的问题,但是使用的时候也要有所考量,根据实际业务考虑系统中有没有其他需求需要用较长的数字,统一用过滤器会不会受到影响。


http://www.niftyadmin.cn/n/660174.html

相关文章

c/c++ const关键字

const 在星号的右边&#xff1a;不可以改指针的指向&#xff0c;可以用指针改里面的值 int * const p; const在星号的左边&#xff1a;可以改指针的指向&#xff0c;不可以用指针改里面的值 int const *p;const int *p; const在星号的两边都有&#xff1a;既不可以改指针的指向…

Lucene笔记二

lucene 的排序 package cn.itcast.lucene;import java.io.IOException;import org.apache.lucene.document.Document; import org.apache.lucene.queryparser.classic.MultiFieldQueryParser; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lu…

手动调整滚动条回归最顶部_一个自适应滚动条的实现

自适应滚动条为了得到一个自适应滚动条&#xff0c;你需要告诉Windows滚动条所覆盖区域的最大值和最小值&#xff0c;当前滚动条的位置以及滚动条滑块(Thumb&#xff0c;也称”页大小”)的大小。一个比较麻烦的方法&#xff0c;是当显示区域最大值是可变的情况。这和GDI中的坐标…

一个故事看懂HTTPS

我是一个浏览器&#xff0c;每到夜深人静的时候&#xff0c;主人就打开我开始学习。 为了不让别人看到浏览记录&#xff0c;主人选择了“无痕模式”。 但网络中总是有很多坏人&#xff0c;他们通过抓包截获我和服务器的通信&#xff0c;主人干了什么&#xff0c;请求了什么数据…

MySQL如何有效的存储IP地址及字符串IP和数值之间如何转换

在看高性能MySQL第3版(4.1.7节)时&#xff0c;作者建议当存储IPv4地址时&#xff0c;应该使用32位的无符号整数(UNSIGNED INT)来存储IP地址&#xff0c;而不是使用字符串。但是没有给出具体原因。为了搞清楚这个原因&#xff0c;查了一些资料&#xff0c;记录下来。 相对字符串…

matplotlib画图——条形图

一.单条 import numpy as np import matplotlib.pyplot as pltN 5 y1 [20, 10, 30, 25, 15] y2 [15, 14, 34 ,10,5] index np.arange(5)bar_width 0.3 plt.bar(index , y1, width0.3 , colory) plt.bar(index , y2, width0.3 , colorb ,bottomy1) plt.show() 二.误差棒 me…

浅谈消息中间件,MQ的来龙去脉

1.什么是消息中间件 MQ全称为Message Queue&#xff0c;消息队列是应用程序和应用程序之间的通信方法。 为什么使用MQ&#xff1f; 在项目中&#xff0c;可将一些无需即时返回且耗时的操作提取出来&#xff0c;进行异步处理&#xff0c;而这种异步处理的方式大大的节省了服务器…

Solidity概述及基本代码展示

Solidity是实施智能合约的契约导向的高级语言。它受到C &#xff0c;Python和JavaScript的影响&#xff0c;旨在针对以太坊虚拟机(EVM)。 Solidity是静态类型的&#xff0c;支持继承&#xff0c;库和复杂的用户定义类型等功能。 您将会看到&#xff0c;可以创建投票&#xff0c…