应使用 java.sql.Timestamp 或 JDBC 4.2+ 的 LocalDateTime 存储带时间的值;java.sql.Date 仅表示日期,构造时即截断时间为00:00:00,导致 Oracle 中时间部分全为零。
java.sql.Date 存进 Oracle 后时间部分全变成 00:00:00
这是最典型的类型误用:用 java.sql.date 存带时分秒的完整时间,它内部只保留年月日,构造时会强制截断时间部分为午夜(00:00:00),且不带时区信息。
常见错误现象:new java.sql.Date(System.currentTimeMillis()) 插入 Oracle 的 DATE 或 TIMESTAMP 字段后,查出来时间总是 00:00:00;或者用 PreparedStatement.setDate() 设置一个含时间的 java.util.Date,结果也被归零。
-
java.sql.Date是java.util.Date的子类,但设计初衷是**仅表示日期(SQL DATE)**,不是“Java 的 Date” - 它重写了
toString(),显示形如2024-05-20,但底层毫秒值其实是当天 00:00:00 的时间戳 —— 这个“归零”在构造时就完成了 - Oracle 的
DATE类型实际包含年月日时分秒(精度到秒),TIMESTAMP更细(可到纳秒),它们都支持存时间,问题出在 Java 端传入的数据本身没带时间
该用 java.sql.Timestamp 还是 LocalDateTime?
取决于你用的是 JDBC 4.2+(推荐)还是老版本,以及是否需要时区语义。
使用场景:你要存精确到秒甚至毫秒的时间点,比如订单创建时间、日志打点。
- JDBC 4.2+(JDK 8+ 默认):直接用
LocalDateTime或ZonedDateTime,调PreparedStatement.setObject(1, localDateTime)即可,驱动自动映射到 OracleTIMESTAMP - 老 JDBC(如 ojdbc6):必须用
java.sql.Timestamp,它是java.util.Date的子类,**明确保留毫秒精度**,对应 Oracle 的TIMESTAMP - 别用
java.sql.Date存带时间的值,也别用java.util.Date直接塞给setDate()—— 它会被转成java.sql.Date再截断
PreparedStatement.setTimestamp() 的三个参数怎么选?
setTimestamp(int parameterIndex, Timestamp x, Calendar cal) 这个三参方法常被误用,尤其在跨时区写入时。
参数差异和影响:
- 两参版
setTimestamp(1, ts):按 JVM 默认时区解释ts的毫秒值,再转成数据库所在时区存储(Oracle 服务器时区)。如果 JVM 和 DB 时区不一致,可能偏移 - 三参版
setTimestamp(1, ts, cal):显式指定Calendar所代表的时区来解析ts。例如Calendar.getInstance(TimeZone.getTimeZone("UTC"))表示把ts当作 UTC 时间写入 - 性能影响:三参版多一次时区转换,但更可控;两参版简单,但依赖环境配置,上线后容易因服务器时区变更出错
Oracle 字段类型选 DATE 还是 TIMESTAMP?
别只看名字,关键看精度需求和是否要时区支持。
兼容性与行为差异:
-
DATE:Oracle 里精度只到秒,无小数秒;不支持时区;java.sql.Timestamp写入时,毫秒部分会被丢弃(四舍五入到秒) -
TIMESTAMP:默认精度到微秒(可设TIMESTAMP(6)),支持毫秒级;若用TIMESTAMP WITH TIME ZONE,还能存时区偏移 - 如果你用
LocalDateTime+ JDBC 4.2+ 写入DATE字段,驱动会静默截断毫秒 —— 不报错,但数据丢了精度 - 生产环境建议统一用
TIMESTAMP,哪怕当前不需要毫秒,也为后续扩展留余地
真正麻烦的不是选哪个类型,而是 Java 端用错对象再配上 Oracle 字段类型不匹配 —— 比如用 java.sql.Date 往 TIMESTAMP 里写,时间照样归零。时区、精度、驱动版本这三块只要有一块没对齐,时间就悄悄变了。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










