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