问题描述
错误的ID,最后两位数都是0,即:
1152921504735848700,
但是,正确的是:
1152921504735848759,
遇到Long类型的数据,传送给前端遇到精度丢失的问题.
后端的订单号值比如1089713253179727872 传给前端 显示出来是1089713253179727900。
拿postman测试接口没有问题,查到的是正确的数据,原来是因为Long类型数据传给前端可能精度丢失,js中的数据类型不能包含所有的java long值。
这个问题真是在前后端连接的夹缝中求生存了,同事跟我说拿返回的数据查不到,我拿postman调后端又完全发现不了问题。话又说回来,用swagger可以发现这个问题的。
后端(Java)数据获取正常,前端显示精度有误。
原因分析
Java Long的范围:
long的最大值:9223372036854775807
long的最小值:-9223372036854775808
大整数的精度丢失和浮点数本质上是一样的,尾数位最大是 52 位,因此 JS 中能精准表示的最大整数是 Math.pow(2, 53),十进制即 9007199254740992。
600040076242144000 > 9007199254740992
9007199254740992
大于 9007199254740992 的会丢失精度。
9007199254740992 >> 10000000000000…000 // 共计 53 个 0
9007199254740992 + 1 >> 10000000000000…001 // 中间 52 个 0
9007199254740992 + 2 >> 10000000000000…010 // 中间 51 个 0
实际上
9007199254740992 + 1 // 丢失
9007199254740992 + 2 // 未丢失
9007199254740992 + 3 // 丢失
9007199254740992 + 4 // 未丢失
解决方案:
本质上是因为long类型在转换中失真了,所以改成String类型的传就没有问题了。
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.util.List;
public class WebConfig extends WebMvcConfigurerAdapter {
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);
jackson2HttpMessageConverter.setObjectMapper(objectMapper);
converters.add(jackson2HttpMessageConverter);
}
}
另外,springboot项目可以添加注解:@JsonSerialize(using = ToStringSerializer.class).
利用JsonSerializer完成注解,也可解决Long类型精度问题:
继承JsonSerializer类
com.fasterxml.jackson.databind.JsonSerializer;
public class JsonLongSerializer extends JsonSerializer<Long> {
public void serialize(Long aLong, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(Long.toString(aLong));
}
}
找到VO类,在Long类型字段上面添加注解
using = JsonLongSerializer.class )
private Long voucherId = null;
(
如果后台的long类型的最大值大于js的number类型,就要使用这个注解:
@JsonSerialize(using= ToStringSerializer.class)
.
涉及到ID,数字的参数,后端还是发字符串类型,比较保险。