1. 博客编写背景

本文章的编写背景:由于在 JDK 8 中,Date、Timestamp 对象已经不推荐使用,所以在公司的新项目上,我计划将 LocalDateTime 使用在新项目中。

由于本人所在项目组,统一的前后端时间的交互方式为时间戳,而时间戳并不能直接被fastJsonjackson 直接转换,所以踩了不少的坑,个人建议有耐心的看完。

实现的效果如下:

  1. 前端传递时间戳
{
"localDateTime": 1584700466000
}
  1. 后端返回时间戳
{
"code": "0",
"desc": "请求成功",
"data": {
"localDateTime": 1584700466000
}
}

========================================================

若是感觉废话比较多,那么直接看标注了【★★★】的即可

个人写这个博客,并不想直接写结论,更多的是想给读者分享踩坑的过程

========================================================


2. LocalDateTime 前端交互

2.1 LocalDateTime 向前端写入时间戳

2.1.1 fastJson 默认的写入格式

本项目使用的是 fastJson 会写前端,我们先看下以下代码

  1. 回写前端的 VO 对象
@Data
public class LocalDateTimeVO {
private LocalDateTime localDateTime;
}
  1. 测试方法
public static void main(String[] args) {
LocalDateTimeVO localDateTimeVO = new LocalDateTimeVO();
localDateTimeVO.setLocalDateTime(LocalDateTime.now());
String json = JSON.toJSONString(localDateTimeVO);
System.out.println(json);
}
  1. 控制台输出
{"localDateTime":"2020-03-12T23:00:28.747"}

从上图中可以看出,服务端并不能正常的返回时间戳给前端。并不符合需求。

2.1.2 更改 fastJson 写入格式,让其回写时间戳 (★★★)

  1. fastJson提供了自定义 json 转换的方法 @JSONFiled,我们添加 serializeUsing,将其指定到我们自定义的序列化控制器即可。
  2. 自定义 fastJson 序列化转换器,重写 ObjectSerializer
/**
* 由于 LocalDateTime 类型在转换 JSON 的时候,并不能被转换为字符串,使用 @JsonFormat 只能转换为指定的 pattern 类型,因此我们需要自定义一个序列化执行器
* LocalDateTime 序列化(将 LocalDateTime类型 转换为 时间戳 返回给前端 )
*
* @author Chimm Huang
* @date 2020/3/7
*/
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);
}
}
}
  1. 使用我们自己写的 fastJson 序列化转换器
@Data
public class LocalDateTimeVO {
@JSONField(serializeUsing = LocalDateTimeSerializer.class)
private LocalDateTime localDateTime;
}
  1. 再次执行测试方法,控制台输出
{"localDateTime":1584026032912}

可以看出,LocalDateTime 已经成功被转换为了时间戳,并且可以返回给前端。

2.2 接收前端传递的时间戳为 LocalDateTimme

2.2.1 Post 请求参数封装

1. LocalDateTime 默认接收的格式

不管我们传递时间戳(1584026032912),还是传递自定义格式("2020-03-13"),在服务端接受的时候,都会报错400。也就是说,传入的格式是错误的,无法被 spring 转换为 LocalDateTime

经过我的粗略测试,我发现,默认的接受格式为 LocalDateTime 特有的格式,即:2020-03-12T23:00:28.747,除此之外都会报400。这种格式与 Date 格式的唯一区别就在于,Date之间是用空格区分的,而 LocalDateTime 是用 T 来区分的。

2. 更改 fastJson 反序列化方法,让其能够转换时间戳为 LocalDateTime(★★★)

  1. fastJson 提供的 @JSONField 注解包括了反序列化转换器的指定,因此,我们重写其方法 ObjectDeserializer
/**
* 由于 时间戳 并不能直接被 fastJSON 转换为 LocalDateTime 类型,因此我们需要自定义一个序列化执行器
* LocalDateTime 反序列化(将前端传递的 时间戳 转换为 LocalDateTime 类型)
*
* @author Chimm Huang
* @date 2020/3/7
*/
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;
}
}
  1. 使用我们自己写的 fastJson 反序列化转换器
@Data
pubcli class LocalDateTimeVO {
@JSONField(serializeUsing = LocalDateTimeSerializer.class, deserializeUsing = LocalDateTimeDeserializer.class)
private LocalDateTime localDateTime;
}
  1. 测试方法
public static void main(String[] args) {
String json = "{\"localDateTime\":1584026032912}";
LocalDateTimeVO localDateTimeVO = JSON.parseObject(json, LocalDateTimeVO.class);
System.out.println(localDateTimeVO);
}
  1. 控制台执行结果展示
LocalDateTimeVO(localDateTime=2020-03-12T23:13:52.912)

可以看出,时间戳成功被 fastJson 接受,并转换为了 LocalDateTime

3. 【坑】更改 SpringBoot 的 @RequestBody 为 fastJson 接收(★★★)

当你看到这个小标题时,肯定会很疑惑,我们项目目前不就是使用的 fastJson

吗?

实际情况经过我测试,得出的结论是,我们在回写前端的时候,是使用 fastJson 进行转换的,但是在接受 Json 的时候,是使用 Spring 默认的 jackson 来接受的,所以这会导致,我们重写了 fastJson 的反序列化方法并未执行。前端传递时间戳给后端,后端报错400。


因此,我们需要更改 spring 默认提供的 jacksonfastJson

/**
* springboot 默认使用的是 jackson 进行 requestBody 请求的封装,该项目切换为使用 fastJson 进行请求封装和响应
* 配置 springboot 使用 fastJson 进行数据的请求接受和响应
*
* @author Chimm Huang
* @date 2020/3/7
*/
@Configuration
public class WebConfig implements WebMvcConfigurer { 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());
}
}

配置完成之后,后端与前端使用时间戳进行交互已完成。

2.2.2 GET 请求参数封装

只需自定义一个转换类即可

/**
* LocalDateTime 作为 作为 RequestParam 或者 PathVariable 时,将前端传递的时间戳转换
*
* @author Chimm Huang
* @date 2020/04/16
*/
@Configuration
public class LocalDateTimeGetConverter {
@Bean
public Converter<String, LocalDateTime> localDateTimeConverter() {
return new Converter<String, LocalDateTime>() {
@Override
public LocalDateTime convert(String source) {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(source)), ZoneId.systemDefault());
}
};
}
}

实例代码:

@GetMapping("/soutTime")
public void soutTimeByGet(LocalDateTime localDateTime) {
System.out.println(localDateTime);
}

前端请求:

localhost:8001/api/demo/soutTime?localDateTime=1587026916000

控制台输出:

2020-04-16T16:48:36

3. LocalDateTime 与数据库交互(★★★)

与数据库交互比较简单,我们使用的 mybatis 的版本为 3.4.5。且数据库时间类型为:datetime

我们只需要在 pom 文件中引入 jsr310 坐标即可

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-typehandlers-jsr310</artifactId>
<version>1.0.2</version>
</dependency>

3.1 【坑】数据库交互LocalDateTime被四舍五入(★★★)

LocalDateTime 是可以精确到纳秒的,但是数据库datetime类型如果不指定长度的话,默认是精确到秒的。这就会造成,在LocalDateTime为最大值的时候,如:2020-04-01T23:59:59.999999999,存入数据库的时候被四舍五入为了2020-04-02 00:00:00

解决方案一:

重置一下LocalDateTime的最大时间,将最大精度设置为秒。

解决方案二:

将数据库的datetime类型长度设置为6(datetime(6)即微秒),然后将LocalDateTime的最大精度重置为对应的微妙即可。

以上两种方案调用LocalDateTimewithNano()方法即可

public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
LocalDateTime todayMax = LocalDateTime.of(now.toLocalDate(), LocalTime.MAX);
// 输出当天的时间
System.out.println(now);
// 输出当天的最大时间(默认最大)
System.out.println(todayMax);
// 输出当天的最大时间(datetime精度为秒的时候)
System.out.println(todayMax.withNano(0));
// 输出当天的最大时间(datetime精度为毫秒的时候) datetime(3)
System.out.println(todayMax.withNano(999000000));
// 输出当天的最大时间(datetime精度为微秒的时候) datetime(6)
System.out.println(todayMax.withNano(999999000));
}

控制台输出

2020-04-01T09:50:46.830845400
2020-04-01T23:59:59.999999999
2020-04-01T23:59:59
2020-04-01T23:59:59.999
2020-04-01T23:59:59.999999

最新文章

  1. Go语言的堆栈分析
  2. VB检测按键CTRL+C的次数
  3. [SoapUI] Post请求Body里面限制特殊字符(&amp;、%),Groovy脚本里特殊字符需要添加“\”转义($)。
  4. The storage wars: Shadow Paging, Log Structured Merge and Write Ahead Logging
  5. 如何让WordPress支持上传更多文件类型
  6. C#:WebBrowser中伪造referer,为何对流量统计器无效?
  7. UVa 11825 (状压DP) Hackers&#39; Crackdown
  8. python学习小结4:类
  9. sdut 2351 In Danger (找规律)
  10. comparable与comparator比较
  11. [Swust OJ 772]--Friend(并查集+map的运用)
  12. node八-核心模块、包
  13. java知识点总结--java数据类型
  14. Go - WaitGroup
  15. Nginx源代码安装
  16. Batch normalization:accelerating deep network training by reducing internal covariate shift的笔记
  17. 使用RT3070使开发板上网
  18. 微信 小程序布局 swiper 页面
  19. Android开发 使用HBuilder的缓存方法
  20. tomcat做成服务

热门文章

  1. c3p0 连接池配置数据源
  2. python之模块、类、对象
  3. 一个学习 Koa 源码的例子
  4. openjdk tomcat 安装
  5. 「雕爷学编程」Arduino动手做(8)——湿度传感器模块
  6. Codeforces1176B(B题)Merge it!
  7. 8.4 Go select
  8. RenderBox使用说明书&amp;原理浅析
  9. FTP上传 -首先上传文件到的那台电脑得安装ftp
  10. 苏浪浪 201771010120 第三周 Java基本程序设计总结