时间类型格式化问题踩坑

记一次时间类型格式化问题:Hutools DbUtil 拼接 SQL 时如何避免日期精度丢失?

一、问题背景

在一次上游服务 SDK 开发中,我们需要通过 Hutools 工具包中的 DbUtil 实现数据持久化。在拼接 SQL 更新语句时,遇到了一个隐蔽的日期格式化问题。核心代码如下:

java 复制代码
Entity.create("table_name")
      .set("start_time", "<= " + xxx.getDate()) // 直接拼接日期对象
      .set("end_time", "IS NULL"));

其中 xxx.getDate() 对应实体类的字段定义如下:

java 复制代码
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS", timezone = "GMT+8")
private Date date; // 期望保留毫秒精度

问题现象

数据库记录中的 start_time 字段丢失了毫秒(SSS)精度,导致时间范围查询出现误差。


二、问题分析

1. 为什么会出现精度丢失?

  • 直接字符串拼接xxx.getDate() 返回 java.util.Date 对象,调用 toString() 方法时默认格式为 EEE MMM dd HH:mm:ss zzz yyyy不包含毫秒信息
  • 注解作用范围@JsonFormat 仅在 Jackson 序列化/反序列化 JSON 时生效,不影响 Date.toString() 的行为。

2. 错误示例分析

java 复制代码
// 假设 date 值为 2023-10-01 12:34:56.789
String sql = "<= " + date.toString();
// 实际拼接结果:<= Sat Oct 01 12:34:56 CST 2023

三、解决方案

1. 手动格式化日期(基础版)

通过 SimpleDateFormat 指定包含毫秒的格式:

java 复制代码
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
String formattedDate = sdf.format(xxx.getDate());

Entity.create("table_name")
      .set("start_time", "<= '" + formattedDate + "'") // 注意添加引号

2. 使用 Hutools 工具类(推荐)

利用 DateUtil.format() 简化操作,避免重复造轮子:

java 复制代码
import cn.hutool.core.date.DateUtil;

String formattedDate = DateUtil.format(xxx.getDate(), "yyyy-MM-dd HH:mm:ss.SSS");

Entity.create("table_name")
      .set("start_time", "<= '" + formattedDate + "'")

四、避坑指南

1. SQL 注入风险

直接拼接字符串存在安全隐患,建议改用 预编译占位符

java 复制代码
// 使用 Hutools 的预编译方式
Entity entity = Entity.create("table_name")
                     .set("start_time", new Condition("<= ?", formattedDate));

2. 时区一致性

确保数据库连接时区与 @JsonFormatGMT+8 一致,避免时差问题。


五、总结

关键点 说明
Date.toString() 默认不包含毫秒,不可依赖其生成 SQL 字符串
@JsonFormat 局限 仅作用于 JSON 序列化,不影响其他场景的日期格式
手动格式化 使用 SimpleDateFormat 或 Hutools 工具类指定明确格式
安全拼接 优先使用预编译语句,避免拼接字符串带来的 SQL 注入风险

最佳实践

  • 始终通过 显式格式化 控制日期字符串的输出。
  • 涉及时间比较时,确保数据库字段类型与精度(如 DATETIME(3))和代码格式一致。
相关推荐
唐青枫18 分钟前
Java Gradle 实战指南:从 Wrapper、Kotlin DSL 到 Spring Boot 多模块构建
java·kotlin·gradle
wuqingshun31415920 分钟前
如何理解Spring Boot中的starter?
java·spring boot·后端
带刺的坐椅21 分钟前
Solon TeamAgent 协作协议:从 SEQUENTIAL 流水线到 HIERARCHICAL 主管团队
java·ai·llm·agent·solon
wuqingshun31415929 分钟前
SpringBoot是如何实现自动配置的
java·spring boot·后端
用户0537040314332 分钟前
我的学习记录泛型
java
zhouhui00134 分钟前
AI帮我写了个Spring Boot校验,线上漏掉了这组边界条件
java·spring boot·redis·ai编程
JRedisX41 分钟前
Java从零手写企业级内存数据库
java
萧瑟余晖43 分钟前
JDK 20 新特性详解
java·开发语言
hdsoft_huge1 小时前
SpringBoot系列17:日志体系Logback配置,日志分割、脱敏、异常堆栈收集规范(生产级落地)
java·spring boot·logback
吃饱了得干活1 小时前
扫码登录如何实现?从原理到实战
java·spring boot·redis