实战:springboot用LocalDateTime快速替换Date

概叙

实战:早点用JDK8中的java.time来替换java.util.Date-CSDN博客

Spring Boot项目中大家从Date升级到LocalDateTime最关心以下两个问题:

  • 使用LocalDateTime类型字段作为接口出入参数,能正常映射转换前端传入的参数吗?返回参数前端是否能收到一个正常的日期时间字段值?

  • 使用LocalDateTime类型作为数据库实体类对象字段,能正常写入数据库和读取吗?简单来说就是看数据库能否正常序列化和反序列化LocalDateTime等Java 8提供的全新日期时间类型。

LocalDateTime 在设计上更加现代化、易用且安全,克服了 Date 和 Calendar 的诸多缺点。虽然它们在功能上有重叠之处,但 LocalDateTime 提供了更好的 API 和功能,推荐在 Java 8 及以上版本中使用新的日期和时间 API。

替换虽然快,但是必要的测试不可少,替换完了记得全网回归测试。

LocalDateTime作为接口出入参数

我也挺好奇的,能不能丝滑地从Date过渡到LocalDateTime,先来看看作为接口出入参数与前端交互是什么个情况。话不多说,直接上示例,

先声明一个用户信息参数对象:

@Data
public class UserParam {
    private Long id;
    private String name;
    private LocalDate birthday;
   private LocalDateDate createTime;
}

接口调用:这里userDTO和上面的参数字段一样的,为了看看接口返回结果罢了

  @PostMapping("/date")
    public UserDTO testLocalDateTime(@RequestBody UserParam param) {
        UserDTO userDTO = new UserDTO();
        BeanUtils.copyProperties(param, userDTO);
        System.out.println(userDTO);
        return userDTO;
    }

没想到执行结果报错~~~出师不利,具体情况如下所示:HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.time.LocalDateTime` from String "2024-06-30 12:12:56":

控制台错误信息:不能正常解析转换成LocalDateTime,这真是怕啥来啥哦。。。

org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.time.LocalDateTime` from String "2024-06-30 12:12:56": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '2024-06-30 12:12:56' could not be parsed at index 10; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.LocalDateTime` from String "2024-06-30 12:12:56": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '2024-06-30 12:12:56' could not be parsed at index 10

接下来说说解决方案:

方案1:在字段属性上加上注解@JsonFormat格式化日期时间,这种方式简单直接

@JsonFormat(shape=JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")

UserParam加上之后控制入参格式和UserDTO加上控制出参格式:

@Data
public class UserParam {
    private Long id;
    private String name;
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd")
    private LocalDate birthday;
   @JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
}

@Data
public class UserDTO {
    private Long id;
    private String name;
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd")
    private LocalDate birthday;
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
}

再次调用上面示例接口就能正常接受参数和返回结果了。

这种方式简单是简单,但就是比较重复不够优雅,一个项目中有这么多接口出入参对象一个个去打上注解不得累个半死,繁琐滴很,能不能一次性搞定,来个全局配置啥的就行那种?肯定有了,下来就来看看。

方案2:全局配置解析LocalDateTime

我们知道Spring Boot 已经为我们提供了日期格式化配置项: spring.jackson.date-format

spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    locale: zh_CN
    time-zone: GMT+8
    default-property-inclusion: non_null

这个配置项我们之前映射Date也是需要配置的,否则日期解析不成功。所以我们只需要在配置类读取该配置项对LocalDateTime的格式化转换即可:

@Configuration
public class LocalDateTimeSerializerConfig {

    @Bean
    public LocalDateTimeSerializer localDateTimeSerializer(JacksonProperties properties) {
        String dateFormat = properties.getDateFormat();
        if (StringUtils.isBlank(dateFormat)) {
            dateFormat = "yyyy-MM-dd HH:mm:ss";
        }
        return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(dateFormat));
    }

    @Bean
    public LocalDateTimeDeserializer localDateTimeDeserializer(JacksonProperties properties) {
        String dateFormat = properties.getDateFormat();
        if (StringUtils.isBlank(dateFormat)) {
            dateFormat = "yyyy-MM-dd HH:mm:ss";
        }
        return new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(dateFormat));

    }

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer(
            LocalDateTimeSerializer localDateTimeSerializer, LocalDateTimeDeserializer localDateTimeDeserializer) {
        return builder -> {
            // 序列化
            builder.serializerByType(LocalDateTime.class, localDateTimeSerializer);
            // 反序列化
            builder.deserializerByType(LocalDateTime.class, localDateTimeDeserializer);
        };
    }

}

上面出入参对象去掉注解@JsonFormat,重新启动项目调用接口发现同样正常输出:

LocalDateTime作为实体类字段正常写入数据库和读取

上面解决了作为接口出入参数映射转换问题,现在来看看作为数据库实体类字段能否正常写入和读取。

实体类user

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "tb_user")
@ExcelIgnoreUnannotated
public class User {
  
    @TableId(type = IdType.AUTO)
    private Long id;
    private String userNo;
    private Integer gender;
    private String name;
    private LocalDate birthday;
    private String phone;
    private String email;
    private Integer isDelete;
    private String address;
    private LocalDateTime createTime;
}

这里我们使用orm框架是:mybatis

public interface UserDAO extends BaseMapper<User> {
}

构建单元测试用例如下:

@SpringBootTest
@RunWith(SpringRunner.class)
public class UserServiceImplTest {

    @Resource
    private UserDAO userDAO;

    @Test
    public void testWriteLocalDateTime() {
        User user = User.builder().id(1L).userNo("001").gender(0).name("张三").phone("12234")
                .birthday(LocalDate.now()).createTime(LocalDateTime.now()).build();
        userDAO.insert(user);
    }
 }

正常写入数据库,开心吧。下面来看看读取:

   @Test
    public void testReadLocalDateTime() {
        User user = userDAO.selectById(1L);
        System.out.println(user);
    }

控制台打印如下:

User(id=1, userNo=001, gender=0, name=张三, birthday=2024-07-03, phone=12234, email=null, isDelete=0, address=null, createTime=2024-07-03T16:09:12)

完美哈。一切都是那么顺风又顺水。那下面我们来看看出问题的情况,把上面的依赖版本改改:

      <!-- MySQL连接驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.26</version>
        </dependency>

        <!-- mp依赖 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>

再次执行测试用例:

@Test
    public void testReadLocalDateTime() {
        User user = userDAO.selectById(1L);
        System.out.println(user);
    }

控制台报错了:

org.springframework.dao.TransientDataAccessResourceException: Error attempting to get column 'birthday' from result set.  Cause: java.sql.SQLException: Conversion not supported for type java.time.LocalDate
; Conversion not supported for type java.time.LocalDate; nested exception is java.sql.SQLException: Conversion not supported for type java.time.LocalDate

是的,我们担心的情况出现了,数据库不能正常序列化转换LocalDate了....

这里不兜圈子了直接说原因吧,mybatis-plus3.5.2是基于mybaits 3.5.10开发的,mybatis3.5.0及其之前是支持对LocalDateTime类型转换,然而从  MyBatis 3.5.1 开始,不再处理 LocalDateTime (还包括:LocalDate 、 LocalTime )类型的转换而是交由 JDBC 组件,也就是 mysql-connector-java 来实现,5.1.26这个版本压根没实现转换,所以报错了。所以最好使用高一点的版本(重点  不建议直接用mybatis-plus;mybatis-plus只是在mybatis的基础上做增强,没有必要图那点方便,mybatis已经够用了 )

     <!-- MySQL连接驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.9</version>
    </dependency>

相关推荐

  1. 实战springbootLocalDateTime快速替换Date

    2024-07-20 10:30:03       19 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-20 10:30:03       103 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-20 10:30:03       114 阅读
  3. 在Django里面运行非项目文件

    2024-07-20 10:30:03       93 阅读
  4. Python语言-面向对象

    2024-07-20 10:30:03       99 阅读

热门阅读

  1. Spark的部署模式

    2024-07-20 10:30:03       27 阅读
  2. Shell 构建flutter + Android 生成Apk

    2024-07-20 10:30:03       22 阅读
  3. 前端面试题日常练-day95 【Less】

    2024-07-20 10:30:03       24 阅读
  4. 前端TS习题

    2024-07-20 10:30:03       21 阅读
  5. 在SAP中,ME22N使用的BAPI是什么

    2024-07-20 10:30:03       24 阅读
  6. AI Native应用中的模型微调

    2024-07-20 10:30:03       29 阅读
  7. 题解/算法 {3219. 切蛋糕的最小总开销 II}

    2024-07-20 10:30:03       16 阅读
  8. web前端 Vue 框架面试120题(一)

    2024-07-20 10:30:03       22 阅读
  9. ceph进程网卡绑定逻辑

    2024-07-20 10:30:03       19 阅读
  10. 网络安全-网络安全及其防护措施12

    2024-07-20 10:30:03       18 阅读
  11. C# 结构体(Struct)

    2024-07-20 10:30:03       24 阅读