LocalDateTime + SpringBoot 整合(Post篇)

149 阅读3分钟

问题复现

当我们使用LocalDateTime 的时候,尤其是在SpringBoot项目中,有哪些注意点呢,一起来看看吧。

假设现在有一个Post请求

@ApiOperation("查询列表")
@PostMapping("queryList")
public ResponseVo queryList(@RequestBody TypeReqVo reqVo){
    System.out.println(reqVo.getCreateTime());
    return null;
}

TypeReqVo

@Data
public class TypeReqVo {

    private LocalDateTime createTime;
}

当我们发一个post请求,看看会怎么样?

image.png

直接报错:JSON parse error: Cannot deserialize value of type java.time.LocalDateTime from String "2025-03-04": Failed to deserialize java.time.LocalDateTime:

配置Jackson

解决办法很简单,只需要加一个配置类

@Configuration
public class JacksonConfig {

    @Bean
    public ObjectMapper objectMapper() {
        // 初始化一个 ObjectMapper 对象,用于自定义 Jackson 的行为
        ObjectMapper objectMapper = new ObjectMapper();

        // 忽略未知属性
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

        // 设置凡是为 null 的字段,返参中均不返回,请根据项目组约定是否开启
        // objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        // 设置时区
        objectMapper.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));

        // JavaTimeModule 用于指定序列化和反序列化规则
        JavaTimeModule javaTimeModule = new JavaTimeModule();

        // 支持 LocalDateTime、LocalDate、LocalTime
        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")));
        // 支持 YearMonth
        javaTimeModule.addSerializer(YearMonth.class, new YearMonthSerializer(DateTimeFormatter.ofPattern("yyyy-MM")));
        javaTimeModule.addDeserializer(YearMonth.class, new YearMonthDeserializer(DateTimeFormatter.ofPattern("yyyy-MM")));

        objectMapper.registerModule(javaTimeModule);

        return objectMapper;
    }
}
  1. 基础配置

    • FAIL_ON_UNKNOWN_PROPERTIES 设置为 false 可避免反序列化时因未知属性抛出异常,符合接口容错性设计‌12。
    • FAIL_ON_EMPTY_BEANS 设置为 false 防止空对象序列化失败,适用于包含无属性 Bean 的场景‌12。
  2. 时区配置

    • setTimeZone("Asia/Shanghai") 明确指定时区,避免因服务器时区差异导致时间转换错误‌12。
  3. 日期时间处理

    • 通过 JavaTimeModule 自定义 LocalDateTimeLocalDate 等类型的格式化规则,覆盖默认的 ISO 格式,统一接口返回的日期格式‌12。
    • 包含 YearMonth 类型的序列化,满足特殊日期格式需求。

因为这两行

javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));

如果你前端传过来的格式为yyyy-MM-dd,那么后台必须用LocalDate,而不是LocalDateTime,所以,我们要这样传递参数:

{
    "createTime": "2025-03-04 00:00:00"
}

发起请求,观察后台日志:

2025-03-04T00:00

成功接收到了,就是打印格式不太好看,但是这个不影响,我直接把这个日期返回出去,看效果。

TypeRespVo

@Data
public class TypeRespVo {

    private LocalDateTime createTime;
}

queryList

@ApiOperation("查询列表")
@PostMapping("queryList")
public ResponseVo queryList(@RequestBody TypeReqVo reqVo){

    System.out.println(reqVo.getCreateTime());

    TypeRespVo respVo = new TypeRespVo();
    respVo.setCreateTime(reqVo.getCreateTime());

    ResponseVo vo = new ResponseVo();
    vo.setData(respVo);
    return vo;
}

测试

image.png

由此可见,返回的格式也是正确的,这就行了。

与MybatisPlus整合

方法论有了,接下来,我们用LocalDateTime的新特性,结合真实数据库来做测试。

核心代码

queryList

public List<TypeRespVo> queryList(TypeReqVo reqVo) {

    if(Objects.nonNull(reqVo.getCreateTimeEnd())) {
        LocalDateTime localDateTime = reqVo.getCreateTimeEnd().plusDays(1);
        reqVo.setCreateTimeEnd(localDateTime);
    }

    List<TypeRespVo> types = baseMapper.selectListNew(reqVo);
    return types;
}

xml文件

<select id="selectListNew" resultType="com.tms.model.resp.TypeRespVo">
    select create_time from type
    <where>
        <if test="req.createTimeStart !=null">
            and create_time >= #{req.createTimeStart}
        </if>
        <if test="req.createTimeEnd !=null">
            and create_time &lt; #{req.createTimeEnd}
        </if>
    </where>

</select>

测试:

假设传入的两个日期都是具体某一天,所以时分秒都是00,由于代码中会自动加一天(左闭右开),所以查询的还是这一天的时间

image.png

PS: 如果前端不想传时分秒,改成yyyy-MM-dd 格式,那么也可以直接用LocalDate。

总结:能不用java.util.Date,就别用了。