原因
这是因为Javascript中数字的精度是有限的,bigint类型的的数字超出了Javascript的处理范围。JS 遵循 IEEE 754 规范,采用双精度存储(double precision),占用 64 bit。其结构如图
各位的含义如下:
- 1位(s) 用来表示符号位
- 11位(e) 用来表示指数
- 52位(f) 表示尾数
尾数位最大是52
位,因此 JS 中能精准表示的最大整数是Math.pow(2, 53)
,十进制即9007199254740992
。而Bigint
类型的有效位数是63位(扣除一位符号位),其最大值为:Math.pow(2,63)
。任何大于9007199254740992
的就可能会丢失精度:
19007199254740992 >> 10000000000000...000 // 共计 53 个 0
29007199254740992 + 1 >> 10000000000000...001 // 中间 52 个 0
39007199254740992 + 2 >> 10000000000000...010 // 中间 51 个 0
实际上值却是:
19007199254740992 + 1 // 丢失
29007199254740992 + 2 // 未丢失
39007199254740992 + 3 // 丢失
49007199254740992 + 4 // 未丢失
解决办法
解决办法很简单将Long类型的字段转为String类型返给前端即可,解决方法有4种可根据自己项目情况来选择:
方式1
在实体类种直接将Long类型的字段修改为String类型
方式2
在要返回的字段上添加@JsonSerialize(using = ToStringSerializer.class)
注解,这样就能在序列化时自动将该字段类型转为String类型返给前端,相较于方式1不影响其他调用该字段的方法
方式3
通过配置全局拦截器将所有返回结果的Long类型字段转为String类型,代码如下:
@EnableWebMvc
@Configuration
public class WebDataConvertConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
/**
* 序列换成json时,将所有的long变成string
* 因为js中得数字类型不能包含所有的java long值
*/
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
objectMapper.registerModule(simpleModule);
// 设置为空的字段不返回
objectMapper.setSerializationInclusion(NON_NULL);
// 指定json转换时间类型的时区
objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
// 指定返回的时间格式
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
jackson2HttpMessageConverter.setObjectMapper(objectMapper);
converters.add(jackson2HttpMessageConverter);
}
}
方式四
配置参数:在yml
或properties
中增加配置,参考博客
spring:
jackson:
generator:
write_numbers_as_strings: true