网站首页 > 技术文章 正文
Java 里的 java.util.Date 和 java.sql.Date 绝对是那种看起来不起眼但能搞得你 Debug 到半夜的“坑王”。
我们先从表面上看,java.sql.Date 是继承自 java.util.Date 的,按理说是“子承父业”,兼容没问题对吧?但实际上,这俩压根儿不是一个世界的。java.util.Date 是 Java 标准库里提供的“通用型”时间类,而 java.sql.Date 则是 JDBC 里为了数据库操作特化出来的版本,核心区别就在于:一个有时间,一个没时间!
就是这么沙雕的区别,java.sql.Date 把时间部分(小时、分钟、秒)全都砍了个干净,只保留年月日。为什么这么干?因为很多数据库的 DATE 类型本来就只存年月日,比如 MySQL 的 DATE 字段。所以 java.sql.Date 就跟着这个“传统艺能”走了,给你个精确到天的“日子”。
来看段代码你就明白了:
public class DateExample {
public static void main(String[] args) {
java.util.Date utilDate = new java.util.Date();
java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());
System.out.println("utilDate: " + utilDate); // 有时间
System.out.println("sqlDate: " + sqlDate); // 只有年月日
}
}
输出可能是这样的:
utilDate: Thu Jun 05 15:30:12 CST 2025
sqlDate: 2025-06-05
你看,时间部分直接没了!这就导致很多时候你把 java.sql.Date 传回前端,前端一看没时间,直接懵了...
我遇到过最离谱的是,有个哥们把 java.sql.Date 强转成 java.util.Date,然后还奇怪为啥结果有问题。讲真,这是个巨坑。虽然它们在继承关系上确实可以强转,但你转完之后,它的“灵魂”还是那个没有时间的 sql.Date
所以你如果真的要拿 java.sql.Date 去做一些带时间的运算,比如和当前时间比较,或者转换成时间戳,那基本就是拿刀割自己手指头。比如这个例子:
java.sql.Date sqlDate = java.sql.Date.valueOf("2025-06-05");
long millis = sqlDate.getTime(); // 你以为这是某个时间点?
Date converted = new Date(millis);
System.out.println(converted); // 时间是 00:00:00 啊兄弟!
你以为你得到了“某一天的某个时刻”,结果其实是“那天的零点”... 这还不如直接用 LocalDate 呢。
对,说到这个就得扯一下 Java 8 的时间新 API——java.time.*。这波更新真的可以说是“Date 的反攻”。像 LocalDate 和 LocalDateTime 就分别对应 java.sql.Date 和 java.util.Date 的干净替代品,前者精确到天,后者精确到秒,而且线程安全,没有时区困扰,API 超清晰,看着就让人舒服。
LocalDate localDate = LocalDate.now();
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println("LocalDate: " + localDate); // 2025-06-05
System.out.println("LocalDateTime: " + localDateTime); // 2025-06-05T15:45:30
最重要的是,java.sql.Date、java.sql.Time 和 java.sql.Timestamp 都有对应的 toLocalDate() 或 toLocalDateTime() 方法,基本可以无痛迁移。
但你要说现实项目里能不能彻底抛弃 java.sql.Date?我只能说:想多了。只要你在用 JDBC,尤其是用 MyBatis 或 Hibernate 这种 ORM 框架,它们底层还是会默认你搞点 java.sql.Date 出来。所以我个人的策略是:入库出库都用 LocalDate / LocalDateTime,中间统一转换。
比如用 MyBatis 的 TypeHandler 机制,写一个 LocalDate 转 java.sql.Date 的 Handler:
@MappedTypes(LocalDate.class)
public class LocalDateTypeHandler extends BaseTypeHandler<LocalDate> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
LocalDate parameter, JdbcType jdbcType) throws SQLException {
ps.setDate(i, Date.valueOf(parameter));
}
@Override
public LocalDate getNullableResult(ResultSet rs, String columnName) throws SQLException {
Date date = rs.getDate(columnName);
return date != null ? date.toLocalDate() : null;
}
// 其他两个 get 方法略
}
配上 XML 或注解,就能实现 LocalDate 的透明持久化,再也不用在每个 Entity 里扔一堆类型转换的代码了
另外顺带一提,还有不少人会问:“那 java.sql.Timestamp 是啥角色?”很简单,它就是 java.util.Date + 纳秒精度的变种,主要是数据库里有精确到秒甚至微秒的 TIMESTAMP 字段时用的。但我一般都尽量避免用它,因为那玩意儿坑更多,特别是跨数据库还经常有精度兼容性问题(MySQL 精确到毫秒,Oracle 精确到纳秒,一导出 CSV 就炸了...)
回到最初的问题:到底 java.util.Date 和 java.sql.Date 有啥区别?
如果用一句话总结就是:
java.util.Date 是全能选手,啥时间信息都有; java.sql.Date 是简化版,为了和数据库的 DATE 类型打配合,砍掉了时间部分,只保留“日期”。
但这区别,常常在你不注意的时候就搞事,所以我现在干脆上来就用 LocalDate 和 LocalDateTime,实在不行中间过渡一下,也总比在项目上线前踩坑强
- 上一篇: 主流CDC工具_cd软件是做什么的
- 下一篇: Hive如何比较两张表所有字段的一致性
猜你喜欢
- 2025-09-02 Hive如何比较两张表所有字段的一致性
- 2025-09-02 主流CDC工具_cd软件是做什么的
- 2024-11-10 软件测试员12小时惊魂记:数据库迁移出大事故,如何测试?
- 2024-11-10 利用Sharding-JDBC解决数据库读写分离查询延时问题
- 2024-11-10 OpenJDK 17 中的 Shenandoah:亚毫秒级 GC 停顿「译」
- 2024-11-10 毫秒级从百亿大表任意维度筛选数据,是怎么做到的……
你 发表评论:
欢迎- 最近发表
-
- Druid 1.2.4 版本发布,增强对 JDK 8 的支持
- Python设计模式 第 1 章 Python 设计模式概述
- RAD Studio 、Delphi或C++Builder设计代码编译上线缩短开发时间
- Hive如何比较两张表所有字段的一致性
- Java 中 java.util.Date 与 java.sql.Date 有什么区别?
- 主流CDC工具_cd软件是做什么的
- 19.提取HFM数据进数据库_怎么提取数据库的信息
- 将Spring Boot应用部署到 Azure_springboot部署到windows
- 这样优化Spring Boot,启动速度快到飞起
- 什么是便携式应用程序,为什么它很重要?
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端aes加密 (58)
- 前端脚手架 (56)
- 前端md5加密 (54)
- 前端路由 (61)
- 前端数组 (73)
- 前端js面试题 (50)
- 前端定时器 (59)
- Oracle RAC (76)
- oracle恢复 (77)
- oracle 删除表 (52)
- oracle 用户名 (80)
- oracle 工具 (55)
- oracle 内存 (55)
- oracle 导出表 (62)
- oracle约束 (54)
- oracle 中文 (51)
- oracle链接 (54)
- oracle的函数 (58)
- oracle面试 (55)
- 前端调试 (52)
本文暂时没有评论,来添加一个吧(●'◡'●)