1.概述
使用LocalDateTime类型字段作为接口出入参数,能正常映射转换前端传入的参数吗?返回参数前端是否能收到一个正常的日期时间字段值?
使用LocalDateTime类型作为数据库实体类对象字段,能正常写入数据库和读取吗?简单来说就是看数据库能否正常序列化和反序列化LocalDateTime等Java 8提供的全新日期时间类型。
2.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;
}
执行结果报错,具体情况如下所示:
接下来说说解决方案:
方案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
@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,重新启动项目调用接口发现同样正常输出:
3.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-plus
<!-- MySQL连接驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.9</version>
</dependency>
<!-- mp依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
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);
}
把上面的依赖版本改改:
<!-- 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这个版本压根没实现转换,所以报错了。所以最好使用高一点的版本
mysql mysql-connector-java 8.0.94.总结
LocalDateTime 在设计上更加现代化、易用且安全,克服了 Date 和 Calendar 的诸多缺点。虽然它们在功能上有重叠之处,但 LocalDateTime 提供了更好的 API 和功能,推荐在 Java 8 及以上版本中使用新的日期和时间 API。