SpringBoot中使用LocalDateTime踩坑记录

文章目录

    • 前言
    • 一、为什么推荐使用java.time包的LocalDateTime而不是java.util的Date?
    • 二、使用LocalDateTime和LocalDate时遇到了哪些坑?
      • 2.1 Redis序列化报错
        • 2.1.1 问题现象
        • 2.1.2 问题分析
        • 2.1.3 解决方案
      • 2.2 LocalDateTime和LocalDate类型的属性返回给前端的值格式不正确
        • 2.2.1 问题现象
        • 2.2.2 解决方案
    • 三、总结

前言

近日心血来潮想做一个开源项目,目标是做一款可以适配多端、功能完备的模板工程,包含后台管理系统和前台系统,开发者基于此项目进行裁剪和扩展来完成自己的功能开发。

本项目基于Java21和SpringBoot3开发,序列化工具使用的是默认的Jackson,使用Spring Data Redis操作Redis缓存。

在定义实体类过程中,日期时间类型的属性我使用了java.time包下的LocalDateLocalDateTime类,而没有使用java.util包下的Date类。

但在使用过程中遇到了一些问题,于是在此记录下来与诸位分享。

一、为什么推荐使用java.time包的LocalDateTime而不是java.util的Date?

LocalDateTime和Date是Java中表示日期和时间的两种不同的类,它们有一些区别和特点。

  • 类型:LocalDateTime是Java 8引入的新类型,属于Java 8日期时间API(java.time包)。而Date是旧版Java日期时间API(java.util包)中的类。
  • 不可变性:LocalDateTime是不可变的类型,一旦创建后,其值是不可变的,对该类对象的加减等计算操作不会修改原对象,而是会返回一个新的LocalDateTime对象。而Date是可变的类型,可以通过方法修改其值。
  • 线程安全性:LocalDateTime是线程安全的,多个线程可以同时访问和操作不同的LocalDateTime实例。而Date是非线程安全的,如果多个线程同时访问和修改同一个Date实例,可能会导致不可预期的结果。
  • 时间精度:LocalDateTime提供了纳秒级别的时间精度,可以表示更加精确的时间。而Date只能表示毫秒级别的时间精度。
  • 时区处理:LocalDateTime默认不包含时区信息,表示的是本地日期和时间。而Date则包含时区信息,它的实际值会受到系统默认时区的影响。

由于LocalDateTime是Java 8及以上版本的新类型,并提供了更多的功能和灵活性,推荐在新的项目中使用LocalDateTime来处理日期和时间。

对于旧版Java项目,仍然需要使用Date类,但在多线程环境下需要注意其线程安全性。

如果需要在LocalDateTime和Date之间进行转换,可以使用相应的方法进行转换,例如通过LocalDateTime的atZone()方法和Date的toInstant()方法进行转换。

二、使用LocalDateTime和LocalDate时遇到了哪些坑?

2.1 Redis序列化报错

2.1.1 问题现象

在使用RedisTemplate向Redis中插入数据时,遇到了如下报错:

2024-01-11T21:33:25.233+08:00 ERROR 13212 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exceptionorg.springframework.data.redis.serializer.SerializationException: Could not write JSON: Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: java.util.ArrayList[0]->com.fast.alden.data.model.SysApiResource["createdTime"])at org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer.serialize(Jackson2JsonRedisSerializer.java:157) ~[spring-data-redis-3.2.0.jar:3.2.0]at org.springframework.data.redis.core.AbstractOperations.rawValue(AbstractOperations.java:128) ~[spring-data-redis-3.2.0.jar:3.2.0]at org.springframework.data.redis.core.DefaultValueOperations.set(DefaultValueOperations.java:236) ~[spring-data-redis-3.2.0.jar:3.2.0]

image.png

2.1.2 问题分析

在使用Redis缓存含有LocalDateTime类型变量的实体类时会产生序列化问题,因为Jackson库在默认情况下不支持Java8的LocalDateTime类型的序列化和反序列化。

错误堆栈中也给出了解决方案,添加 com.fasterxml.jackson.datatype:jackson-datatype-jsr310依赖,但光添加依赖是不够的,还我们需要自定义序列化和反序列化的行为。

2.1.3 解决方案
  1. 添加maven依赖
<dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jsr310</artifactId><version>2.13.0</version>
</dependency>
  1. 修改RedisSerializer Bean配置

在定义RedisSerializer Bean的代码中自定义ObjectMapper对象处理时间属性时的序列化和反序列化行为,LocalDateLocalDateTimeLocalTime的序列化和反序列化都要自定义,还要禁用将日期序列化为时间戳。

@Configuration
public class RedisConfig {@Beanpublic RedisSerializer<Object> redisSerializer() {ObjectMapper objectMapper = new ObjectMapper();objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);// 必须设置,否则无法将JSON转化为对象,会转化成Map类型objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);// 自定义ObjectMapper的时间处理模块JavaTimeModule javaTimeModule = new JavaTimeModule();javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));objectMapper.registerModule(javaTimeModule);// 禁用将日期序列化为时间戳的行为objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);//创建JSON序列化器return new Jackson2JsonRedisSerializer<>(objectMapper, Object.class);}
}

2.2 LocalDateTime和LocalDate类型的属性返回给前端的值格式不正确

2.2.1 问题现象

在application.yml中设置了全局的日期类型的序列化和反序列化格式,在对应字段上也并没有使用@JsonFormat进行特殊设置,但是LocalDateTime类型的属性返回给前端时并没有生效,返回的仍是LocalDateTime默认的ISO标准时间格式的字符串。

spring:jackson:date-format: yyyy-MM-dd HH:mm:sstime-zone: GMT+8default-property-inclusion: alwaysmvc:format:date-time: yyyy-MM-dd HH:mm:ssdate: dd/MM/yyyy

image.png

2.2.2 解决方案

自定义Jackson配置,代码如下:

@Configuration
public class JacksonConfig {@Beanpublic Jackson2ObjectMapperBuilderCustomizer customizer() {return builder ->builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss")// long类型转string, 前端处理Long类型,数值过大会丢失精度.serializerByType(Long.class, ToStringSerializer.instance).serializerByType(Long.TYPE, ToStringSerializer.instance).serializationInclusion(JsonInclude.Include.NON_NULL)//指定反序列化类型,也可以使用@JsonFormat(pattern = "yyyy-MM-dd")替代。主要是mvc接收日期时使用.deserializerByType(LocalTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss"))).deserializerByType(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))).deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))// 日期序列化,主要返回数据时使用.serializerByType(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss"))).serializerByType(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))).serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));}
}

三、总结

在使用java.time API的过程中,除了会遇到前文所说的序列化问题之外,可能还会遇到以下问题:

  • 时区问题:LocalDateTime不包含时区信息,这可能导致在不同时区的用户之间出现不一致性。为了避免这个问题,您应该考虑使用ZonedDateTime或OffsetDateTime,并确保在处理日期和时间时考虑时区。
  • 数据库交互:当与数据库交互时,要确保数据库列的数据类型与正在使用的Java日期类型相匹配。例如,如果使用的是PostgreSQL,则可能需要使用timestamp without time zone列类型来存储日期和时间。
  • 默认值和验证:在某些情况下,可能希望为日期或时间字段设置默认值或进行验证。使用Spring的验证注解(如@NotNull或@Size)可以帮助我们确保输入的有效性。
  • 跨时区处理:由于LocalDateTime不包含时区信息,当与全球用户互动时,需要特别注意时区转换。考虑使用像Joda-Time这样的库来帮助我们处理复杂的时区转换。
  • 处理过去和未来的日期:在处理历史事件或计划未来的活动时,请确保我们的应用程序能够正确地处理这些日期。考虑使用像Period或Duration这样的类来计算日期之间的差异。

我也会及时的更新后续实践中所遇到的问题,希望与诸位看官一起进步。

如有错误,还望批评指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/614207.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

类型特质和静态断言

static_assert( constant-expression, string-literal );static_assert( constant-expression ); // C17 (Visual Studio 2017 and later) constant-expression 可以转换为布尔值的整型常量表达式。 如果计算出的表达式为零 (false)&#xff0c;则显示 string-literal 参数&…

PyTorch|view(),改变张量维度

在构建自己的网络时&#xff0c;了解数据经过每个层后的形状变化是必须的&#xff0c;否则&#xff0c;网络大概率会出现问题。PyToch张量有一个方法&#xff0c;叫做view(),使用这个方法&#xff0c;我们可以很容易的对张量的形状进行改变&#xff0c;从而符合网络的输入要求。…

React 18中新钩子 useDeferredValue 使用

React是一个流行的用于构建用户界面的JavaScript库,它不断发展以为开发人员提供优化性能的工具。 React 18中引入的此类工具之一是useDeferredValue钩子,它旨在通过优先渲染更新来提高应用程序的性能。 useDeferredValue钩子是什么? useDeferredValue钩子是React性能优化工…

SAP PP配置学习(五)

查找 四、 其它 设置 MM 过帐号码范围 定义凭证号码范围 OB52 打开期间 MMPV 开帐 &#xff08;下篇见&#xff09;

K-【学习Diffusers 四】 读取模型参数 bin格式、safetensors格式

该操作多用于推理 safetensors格式的参数读取方法 1 拿到pipeline中的unet的办法 unet pipeline.pipe.unet 2 safetensors格式文件的参数读取方法 state_dict safetensors.torch.load_file(args.model_id, device"cpu") unet.load_state_dict(state_dict) # 读入…

随心玩玩(十二)通义千问——LLM大模型微调

写在前面&#xff1a;使劲的摸鱼&#xff0c;摸到的鱼才是自己的~ 文章目录 简介环境配置模型加载jupyter远程配置快速使用微调示例部署方案总结附录&#xff1a; ReAct Prompting 示例准备工作一&#xff1a;样例问题、样例工具准备工作二&#xff1a;ReAct 模版步骤一&#x…

MySQL:DML数据操作语言(添加,删除,修改),DDL数据查询语言(条件查询,分组查询,排序查询,分页查询)

目录 1.DML&#xff08;数据操作语言&#xff09;1.添加数据2.修改数据3.删除数据 2.DQL(数据查询语言)1.DQL-语法2.基本查询3.条件查询(WHERE)1.语法&#xff1a;2.条件&#xff1a;3.案例: 4.聚合函数1.介绍2.常见聚合函数3.语法4.案例 5.分组查询&#xff08;GROUP BY&#…

物联网通讯协议NB-lot和LoRa差异分析

像把大象装冰箱一样&#xff0c;物联网&#xff0c;万物互联也是要分步骤的。 一、感知层(信息获取层)&#xff0c;即利用各种传感器等设备随时随地获取物体的信息; 二、网络层(信息传输层)&#xff0c;通过各种电信网络与互联网的融合&#xff0c;将物体的信息实时准确地传递…

力扣LCR 166. 珠宝的最高价值(java 动态规划)

Problem: LCR 166. 珠宝的最高价值 文章目录 解题思路思路解题方法复杂度Code 解题思路 思路 改题目与本站64题实质上是一样的&#xff0c;该题目在64题的基础上将求取最小路径和改成了求取最大路径和。具体实现思路如下&#xff1a; 1.定义一个int类型的二维数组dp大小为给定…

17- Echarts 配置系列之:单轴 singleAxis

singleAxis&#xff1a; 用于展示只有一个数据维度的数据。它通常用于展示时间序列数据或者数值序列数据。 对于单轴的应用和绘制&#xff0c;其实就相当于我们平时的直角坐标系少一个 X 或者 Y &#xff0c;然后进行图形绘制。 注意&#xff1a; 1.在使用单轴时&#xff0…

1.10号io网络

信号量&#xff08;信号灯集&#xff09; 1> 信号灯集主要完成进程间同步工作&#xff0c;将多个信号灯&#xff0c;放在一个信号灯集中&#xff0c;每个信号灯控制一个进程 2> 每个灯维护了一个value值&#xff0c;当value值等于0时&#xff0c;申请该资源的进程处于阻…

【Python学习】Python学习13-日期和时间

目录 【Python学习】Python学习13-日期和时间 前言通过time 获取时间戳时间元组获取当前时间&#xff0c;格式化时间格式化时间转换python中时间日期格式化符号获取日历Time 模块日历&#xff08;Calendar&#xff09;模块其他模块参考 文章所属专区 Python学习 前言 本章节主…

小马识途:十个营销故事 启发营销思路

在营销过程中&#xff0c;优势是相对的&#xff0c;只有凭借着客观的营销环境创造优势才能够取胜市场。在企业营销中&#xff0c;狗猛酒酸的案例比比皆是。接下来&#xff0c;就与小马识途一起来看看十个经典的营销故事吧&#xff01; 一、摩托车公司 有家德国摩托车公司&…

SQL优化小技巧

在表中建⽴索引&#xff0c;优先考虑 where group by 使⽤到的字段。 查询时尽量避免使⽤select * &#xff0c;只查询需要⽤到的字段。 避免在where⼦句中使⽤关键字两边都是%的模糊查询&#xff0c;尽量在关键字后使⽤模糊查询。 尽量避免在where⼦句中使⽤IN 和NOT IN。 …

【排序】快速排序(C语言实现)

文章目录 前言1. Hoare思想2. 挖坑法3. 前后指针法4. 三路划分5. 快速排序的一些小优化5.1 三数取中常规的三数取中伪随机的三数取中 5.2 小区间优化 6. 非递归版本的快排7. 快速排序的特性总结 前言 快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法&#xff0c;其…

【开源项目】轻量元数据管理解决方案——Marquez

大家好&#xff0c;我是独孤风。 又到了本周的开源项目推荐。最近推荐的元数据管理项目很多&#xff0c;但是很多元数据管理平台的功能复杂难用。 那么有没有轻量一点的元数据管理项目呢&#xff1f; 今天为大家推荐的开源项目&#xff0c;就是一个轻量级的元数据管理工具。虽然…

Linux入门攻坚——12、Linux网络属性配置相关知识2

CentOS 7网络属性配置&#xff1a; 传统命名机制&#xff1a;以太网eth[0,1,2,...]&#xff0c;wlan[0,1,2...] 可预测功能的命名机制&#xff1a; udev支持多种不同的命名方案&#xff1a; Firmware &#xff0c;拓扑结构 在对待设备文件这块&#xff0c;Linux改…

人生重开模拟器(c语言)

前言&#xff1a; 人生重开模拟器是前段时间非常火的一个小游戏&#xff0c;接下来我们将一起学习使用c语言写一个简易版的人生重开模拟器。 网页版游戏&#xff1a; 人生重开模拟器 (ytecn.com) 1.实现一个简化版的人生重开模拟器 &#xff08;1&#xff09; 游戏开始的时…

云HIS系统源码,基层卫生HIS系统,云端SaaS模式多医院版

系统介绍&#xff1a; 基层卫生健康云HIS系统采用云端SaaS服务的方式提供&#xff0c;使用用户通过浏览器即能访问&#xff0c;无需关注系统的部署、维护、升级等问题&#xff0c;系统充分考虑了模板化、配置化、智能化、扩展化等设计方法&#xff0c;覆盖了基层医院的主要工作…

基于OpenMV与STM32的数据通信项目(代码开源)

前言&#xff1a;本文为手把手教学 OpenMV 与 STM32 的数据通信项目教程&#xff0c;本教程使用 STM32F103C8T6 与 OpenMV 进行操作。 OpenMV 是非常强大的计算机视觉实现工具&#xff0c;自身提供了非常多的视觉项目案例&#xff0c;编程与使用门槛极低。为了进一步增强作品的…