现象

前端js获取id然后查询详情信息,发现查询不到结果,然而id值时后端传给前台的,前端获取到id值然后到后端去请求,发现查询不到结果,然后代码跟踪,发现前端传的id参数值和后端传回的值不一样,后面几位莫名其妙变成0了。

原因

js long过大会导致精度丢失。(大整数的精度丢失和浮点数本质上是一样的,尾数位最大是 52 位,因此 JS 中能精准表示的最大整数是 Math.pow(2, 53),十进制即 9007199254740992。大于 9007199254740992 的可能会丢失精度)

解决思路

后端返回json格式数据的,对id进行转换,转换成字符串,这样避免的js long精度丢失问题。

解决办法

springMVC项目

第一步:自定义消息转换器

自定义消息转换器,转换long型为String型,代码如下所示:

  1. import java.io.IOException;
  2. import java.math.BigInteger;
  3. import java.sql.Timestamp;
  4. import java.text.SimpleDateFormat;
  5. import java.util.Date;
  6. import java.util.TimeZone;
  7. import org.springframework.stereotype.Component;
  8. import com.fasterxml.jackson.core.JsonGenerator;
  9. import com.fasterxml.jackson.core.JsonProcessingException;
  10. import com.fasterxml.jackson.databind.JsonSerializer;
  11. import com.fasterxml.jackson.databind.ObjectMapper;
  12. import com.fasterxml.jackson.databind.SerializerProvider;
  13. import com.fasterxml.jackson.databind.module.SimpleModule;
  14. /**
  15. * @Title CustomObjectMapper.java
  16. * @Description
  17. */
  18. @Component("customObjectMapper")
  19. public class CustomObjectMapper extends ObjectMapper {
  20. private static final long serialVersionUID = 1L;
  21. public CustomObjectMapper() {
  22. // 日期转换
  23. SimpleModule module = new SimpleModule();
  24. module.addSerializer(Date.class, new JsonSerializer<Date>() {
  25. @Override
  26. public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider arg2) throws IOException, JsonProcessingException {
  27. SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
  28. jsonGenerator.writeString(sdf1.format(date));
  29. }
  30. });
  31. this.registerModule(module);
  32. // Long to String
  33. SimpleModule module2 = new SimpleModule();
  34. module2.addSerializer(Long.class, new JsonSerializer<Long>() {
  35. @Override
  36. public void serialize(Long value, JsonGenerator jsonGenerator, SerializerProvider arg2) throws IOException, JsonProcessingException {
  37. if (value != null) {
  38. jsonGenerator.writeString(value.toString());
  39. }
  40. }
  41. });
  42. this.registerModule(module2);
  43. // 设置时区
  44. this.setTimeZone(TimeZone.getDefault());// getTimeZone("GMT+8:00")
  45. }
  46. }

第二步:mvc xml中配置自定义消息转换器

clipboard.png

第三步:mybatis mapper xml文件中返回resultType=”map”,申明javaType

PS:经过几番测试,如果resultType=”map”,不会调用自定义Long to String转换器模块,只有自定义返回map,并且申明javaType=”java.lang.Long”,才会走自定义Long to String转换器模块,因为mybatis默认是BigDecimal。
clipboard.png

springBoot项目

第一步:自定义转换器继承jackson2HttpMessageConverter

自定义转换器继承jackson2HttpMessageConverter,自定义实例化MappingJackson2HttpMessageConverterbean,代码如下所示:

  1. package com.ruoyi.framework.config;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
  5. import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
  6. import com.fasterxml.jackson.annotation.JsonInclude;
  7. import com.fasterxml.jackson.databind.MapperFeature;
  8. import com.fasterxml.jackson.databind.ObjectMapper;
  9. import com.fasterxml.jackson.databind.module.SimpleModule;
  10. import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
  11. /**
  12. * Jackson配置
  13. */
  14. @Configuration
  15. public class JacksonConfig {
  16. @Bean
  17. public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() {
  18. final Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
  19. builder.serializationInclusion(JsonInclude.Include.NON_NULL);
  20. final ObjectMapper objectMapper = builder.build();
  21. SimpleModule simpleModule = new SimpleModule();
  22. // Long 转为 String 防止 js 丢失精度
  23. simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
  24. objectMapper.registerModule(simpleModule);
  25. // 忽略 transient 关键词属性
  26. objectMapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true);
  27. return new MappingJackson2HttpMessageConverter(objectMapper);
  28. }
  29. }

第二步:mybatis mapper xml文件中返回resultType=”map”,申明javaType

PS:经过几番测试,如果resultType=”map”,不会调用自定义Long to String转换器模块,只有自定义返回map,并且申明javaType=”java.lang.Long”,才会走自定义Long to String转换器模块,因为mybatis默认是BigDecimal。
clipboard.png

参考网址

https://doc.ruoyi.vip/ruoyi/other/faq.html#如何处理long类型精度丢失问题