浏览器中的 preview 和 response 的值不一致和精度问题解决
今天遇到一个问题,数据库数据无误,接口请求数据无误,但在浏览器发起请求后,接口数据在浏览器的 preview
和 response
中显示不一致。数据库中的数据类型是 bigint
,实体类对应的数据类型是 Long
。问题表现如下:
查询数据库的入口和出口加了日志 显示出来都是对的 但是最终返回到浏览器中确不对了
原因
JavaScript
的 Number
类型不能完全表示 Java
的 Long
类型数字。当 Long
类型的长度超过 17
位时,会出现精度丢失的问题。浏览器在解析超过 17
位的数字时,超出的部分会被转换为 0,导致显示不一致。具体原因如下:
- JavaScript Number 精度限制:
JavaScript
中的Number
类型是双精度浮点数,可以安全地表示的整数范围是-9007199254740991
到9007199254740991
(即15
位数字)。当数字长度超过15
位时,会发生精度丢失,超过17
位的部分则会直接变成0
。 - 精度丢失示例:例如,
Java
的Long
类型值为1816022064764096513
,但在JavaScript
中,超过17
位的部分可能会变成1816022064764096000
。
解决方案
为了避免精度丢失,可以在后台将超过精度的 Long
型数据转换为 String
类型。这确保了在传输过程中数字不会丢失精度,并且前端可以正确地显示这些数据。
修改方式一:【强烈推荐】
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.io.IOException;/*** Description: [解决 Jackson 导致 Long 型数据精度丢失问题]* Author: [mobaijun]* Date: [2024/8/1 15:58]* IntelliJ IDEA Version: [IntelliJ IDEA 2023.1.4]*/
@Configuration
public class JacksonConfig {// JavaScript 最大安全整数private static final long JS_MAX_SAFE_INTEGER = 9007199254740991L; // 2^53-1private static final long JS_MIN_SAFE_INTEGER = -9007199254740991L;@Beanpublic Jackson2ObjectMapperBuilderCustomizer smartLongSerialization() {return builder -> {builder.serializerByType(Long.class, new SmartLongSerializer());builder.serializerByType(Long.TYPE, new SmartLongSerializer());};}/*** 智能 Long 序列化器:* 1. 当值在JS安全范围内时,保持数字形式* 2. 当值超出安全范围时,转为字符串*/public static class SmartLongSerializer extends JsonSerializer<Long> {@Overridepublic void serialize(Long value, JsonGenerator gen, SerializerProvider provider)throws IOException {if (value != null &&(value > JS_MAX_SAFE_INTEGER || value < JS_MIN_SAFE_INTEGER)) {// 超出安全范围,转为字符串gen.writeString(value.toString());} else {// 在安全范围内,保持数字形式gen.writeNumber(value);}}}
}
修改方式二:【不建议】
对于超过 JavaScript 安全整数范围(2^53-1)的数字:
@JsonSerialize(using = ToStringSerializer.class)
private BigInteger bigNumber; // 对于超大数字使用BigInteger
方式二是个本版本,只能全部都加。不建议使用
方式一统一处理:推荐使用第一种全局配置方案,这样整个应用中的所有超过进度的 Long 类型都会自动转为字符串