现象
前端js获取id然后查询详情信息,发现查询不到结果,然而id值时后端传给前台的,前端获取到id值然后到后端去请求,发现查询不到结果,然后代码跟踪,发现前端传的id参数值和后端传回的值不一样,后面几位莫名其妙变成0了。
原因
js long过大会导致精度丢失。(大整数的精度丢失和浮点数本质上是一样的,尾数位最大是 52 位,因此 JS 中能精准表示的最大整数是 Math.pow(2, 53),十进制即 9007199254740992。大于 9007199254740992 的可能会丢失精度)
解决思路
后端返回json格式数据的,对id进行转换,转换成字符串,这样避免的js long精度丢失问题。
解决办法
springMVC项目
第一步:自定义消息转换器
自定义消息转换器,转换long型为String型,代码如下所示:
import java.io.IOException;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
/**
* @Title CustomObjectMapper.java
* @Description
*/
@Component("customObjectMapper")
public class CustomObjectMapper extends ObjectMapper {
private static final long serialVersionUID = 1L;
public CustomObjectMapper() {
// 日期转换
SimpleModule module = new SimpleModule();
module.addSerializer(Date.class, new JsonSerializer<Date>() {
@Override
public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider arg2) throws IOException, JsonProcessingException {
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
jsonGenerator.writeString(sdf1.format(date));
}
});
this.registerModule(module);
// Long to String
SimpleModule module2 = new SimpleModule();
module2.addSerializer(Long.class, new JsonSerializer<Long>() {
@Override
public void serialize(Long value, JsonGenerator jsonGenerator, SerializerProvider arg2) throws IOException, JsonProcessingException {
if (value != null) {
jsonGenerator.writeString(value.toString());
}
}
});
this.registerModule(module2);
// 设置时区
this.setTimeZone(TimeZone.getDefault());// getTimeZone("GMT+8:00")
}
}
第二步:mvc xml中配置自定义消息转换器
第三步:mybatis mapper xml文件中返回resultType=”map”,申明javaType
PS:经过几番测试,如果resultType=”map”,不会调用自定义Long to String转换器模块,只有自定义返回map,并且申明javaType=”java.lang.Long”,才会走自定义Long to String转换器模块,因为mybatis默认是BigDecimal。
springBoot项目
第一步:自定义转换器继承jackson2HttpMessageConverter
自定义转换器继承jackson2HttpMessageConverter,自定义实例化MappingJackson2HttpMessageConverterbean,代码如下所示:
package com.ruoyi.framework.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
/**
* Jackson配置
*/
@Configuration
public class JacksonConfig {
@Bean
public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() {
final Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
final ObjectMapper objectMapper = builder.build();
SimpleModule simpleModule = new SimpleModule();
// Long 转为 String 防止 js 丢失精度
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
objectMapper.registerModule(simpleModule);
// 忽略 transient 关键词属性
objectMapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true);
return new MappingJackson2HttpMessageConverter(objectMapper);
}
}
第二步:mybatis mapper xml文件中返回resultType=”map”,申明javaType
PS:经过几番测试,如果resultType=”map”,不会调用自定义Long to String转换器模块,只有自定义返回map,并且申明javaType=”java.lang.Long”,才会走自定义Long to String转换器模块,因为mybatis默认是BigDecimal。