前言:最近在跟着视频做 JAVA 入门项目,故开启这一系列的笔记,这也是我第一次进行系统性总结,我将会在这一系列的文章中记录我开发中遇到的问题,希望能帮到有遇到同样问题的人,当然如果有错的还请大家多多指教。
一:问题引入
在生成用户 id 时候,我们经常使用雪花算法来生成 id,以确保每个用户的 id 是不重复的,但是在根据 id 对数据进行更改操作时候,会发现程序不报错但是却没有更改成功的情况,不通过断点调试还真不知道哪里出了问题,下面我将演示这一过程:刚进入主界面按分页显示数据时候,按 F12 通过浏览器的调式工具查看响应回来的数据:
对比数据库中的数据:
可以看到响应回来的是正确的数据,但是如果这时候我想修改员工的状态信息,查看发送的数据:
打印日志:
可以看到这时候传过去的 id 是不正确的,这就会造成修改不成功但是不会报错。
二:原因
为什么会出现这样的情况呢?原因是分页查询时候服务端响应给页面的数据 id 的值为 19 位数字,类型为 Long,而 JS 处理 Long 型数字只能精确到前 16 位,最后三位会进行四舍五入处理,所以最终通过 ajax 请求提交给服务端的时候 id 会丢失精度。下面验证这一现象,验证方法也很简单,我们只需要 alert 一个 Long 型数字即可。执行代码 alert(1234567891234567891)
可以看到精度丢失了。
三:解决方案
在服务端给客户端页面响应数据时候,会用到 Spring mvc 的消息转换器,我们可以扩展这个消息转换器。既然 JS 处理 Long 型数据精度会丢失,那么我们可以在服务端给页面响应返回 JSON 数据时候进行处理,将 Long 型数据转换为 String 类型再进行返回就能确保到时候再传回来时精度不丢失。具体实现步骤:1.提供对象转换器 JacksonObjectMapper,基于 Jackson 进行 Java 对象到 Json 数据转换,此映射器除了能把 Java 对象转换为 json 数据之外,还能将日期类型数据进行转换以更方便查看。
package com.my.pro1.common;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
/**
* 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
* 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
* 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
*/
public class JacksonObjectMapper extends ObjectMapper {
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
public JacksonObjectMapper() {
super();
//收到未知属性时不报异常
this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
//反序列化时,属性不存在的兼容处理
this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
SimpleModule simpleModule = new SimpleModule()
.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))
.addSerializer(BigInteger.class, ToStringSerializer.instance)
.addSerializer(Long.class, ToStringSerializer.instance) //处理Long型数据
.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
//注册功能模块 例如,可以添加自定义序列化器和反序列化器
this.registerModule(simpleModule);
}
}
复制代码
2.在 WebMvcConfig 配置类中扩展 Spring mvc 的消息转换器,在此消息转化器中使用提供的对象转换器进行 Java 对象到 json 数据转换。
package com.my.pro1.config;
import com.my.reggie.common.JacksonObjectMapper;
import lombok.extern.slf4j.Slf4j;
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.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.util.List;
@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
/**
* 设置静态资源映射
*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
}
/**
* 拓展mvc框架的消息转换器
* @param converters
*/
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//创建消息转换器对象
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
//设置具体的对象映射器
messageConverter.setObjectMapper(new JacksonObjectMapper());
//通过设置索引,让自己的转换器放在最前面,否则默认的jackson转换器会在前面,用不上自己配置的转换器
converters.add(0,messageConverter);
}
}
复制代码
这里要注意最后一步要将自己的转换器放在最前面,否则默认的 jackson 转换器会在前面,用不上自己配置的转换器
四:处理结果
这时候再执行更新操作
/**
* 修改员工状态
* @param employee //传入的员工id及状态信息
* @return //R<String>
*/
@PutMapping
public R<String> updateStatus(HttpServletRequest request,@RequestBody Employee employee){
Long id = (Long) request.getSession().getAttribute("employee");
employee.setUpdateUser(id);
employee.setUpdateTime(LocalDateTime.now());
log.info(employee.toString());
boolean b = employeeService.updateById(employee);
if(b){
return R.success("修改成功!");
}
return R.error("修改失败!");
}
复制代码
可以看到传过来的参数没有问题,至此问题解决。
评论