解决黑马点评项目中实现商铺缓存与数据库的双写一致问题出现的问题:时间问题
2023-01-10 10:41:48.177 ERROR 1796 — [nio-8081-exec-2] com.hmdp.config.WebExceptionAdvice : org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: raw timestamp (1642069999999) not allowed for java.time.LocalDateTime
: need additional information such as an offset or time-zone (see class Javadocs); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: raw timestamp (1642069999999) not allowed for java.time.LocalDateTime
: need additional information such as an offset or time-zone (see class Javadocs)
at [Source: (PushbackInputStream); line: 9, column: 17] (through reference chain: com.hmdp.entity.Shop[“updateTime”])
Java的LocalDateTime 转为 前端的时间戳
/**
* 由于 LocalDateTime 类型在转换 JSON 的时候,并不能被转换为字符串,使用 @JsonFormat 只能转换为指定的 pattern 类型,因此我们需要自定义一个序列化执行器
* LocalDateTime 序列化(将 LocalDateTime类型 转换为 时间戳 返回给前端 )
*
*/
public class LocalDateTimeSerializer implements ObjectSerializer {
@Override
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
if (object != null) {
LocalDateTime localDateTime = (LocalDateTime) object;
//将localDateTime转换为中国区(+8)时间戳。
serializer.write(localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
} else {
serializer.write(null);
}
}
}
前端的时间戳 转为 Java的LocalDateTime
/**
* 由于 时间戳 并不能直接被 fastJSON 转换为 LocalDateTime 类型,因此我们需要自定义一个序列化执行器
* LocalDateTime 反序列化(将前端传递的 时间戳 转换为 LocalDateTime 类型)
*
*/
public class LocalDateTimeDeserializer implements ObjectDeserializer {
@Override
@SuppressWarnings("unchecked")
public LocalDateTime deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
String timestampStr = parser.getLexer().numberString();
if (timestampStr == null || "".equals(timestampStr)) {
return null;
}
timestampStr = timestampStr.replaceAll("\"", "");
long timestamp = Long.parseLong(timestampStr);
if(timestamp == 0) {
return null;
}
return Instant.ofEpochMilli(timestamp).atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
}
@Override
public int getFastMatchToken() {
return 0;
}
}
使用
更改 SpringBoot 的 @RequestBody 为 fastJson 接收
我们在回写前端的时候,是使用 fastJson 进行转换的,但是在接受 Json 的时候,是使用 Spring 默认的 jackson 来接受的,所以这会导致,我们重写了 fastJson 的反序列化方法并未执行。前端传递时间戳给后端,后端报错400。
因此,我们需要更改 spring 默认提供的 jackson 为 fastJson
package com.hmdp.config;
@Configuration
public class MvcConfig implements WebMvcConfigurer {
/**
* springboot 默认使用的是 jackson 进行 requestBody 请求的封装,该项目切换为使用 fastJson 进行请求封装和响应
* 配置 springboot 使用 fastJson 进行数据的请求接受和响应
*
*/
public HttpMessageConverter<String> stringConverter() {
return new StringHttpMessageConverter(StandardCharsets.UTF_8);
}
public FastJsonHttpMessageConverter fastConverter() {
//1、定义一个convert转换消息的对象
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
//2、添加fastJson的配置信息
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(
SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteNullStringAsEmpty,
SerializerFeature.WriteNullNumberAsZero,
SerializerFeature.WriteNullListAsEmpty,
SerializerFeature.WriteNullBooleanAsFalse);
fastJsonConfig.setCharset(StandardCharsets.UTF_8);
//2-1 处理中文乱码问题
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
fastConverter.setSupportedMediaTypes(fastMediaTypes);
//3、在convert中添加配置信息
fastConverter.setFastJsonConfig(fastJsonConfig);
return fastConverter;
}
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.clear();
converters.add(stringConverter());
converters.add(fastConverter());
}
}