在 java.util.Date 或 java.sql.Date 之间进行选择
Posted
技术标签:
【中文标题】在 java.util.Date 或 java.sql.Date 之间进行选择【英文标题】:Choosing between java.util.Date or java.sql.Date 【发布时间】:2014-08-30 06:32:36 【问题描述】:我应该使用 java.util.Date 还是 java.sql.Date?
我有一个 VisualFox 数据库,并且我使用 IntelliJ Idea 向导使用适当的 jdbc 类型 4 驱动程序检索了实体。
ide(或驱动程序)已将日期字段创建为时间戳。但是,日期字段不是时间戳,而是日期字段,它们仅存储年、月和日。
所以我想知道是否应该切换到 java.util.Date 或 java.sql.Date。乍一看,我认为 java.sql.Date 应该是合适的,但它有许多声明为已弃用的方法。
【问题讨论】:
你使用的 JPA 映射是?例如使用@Temporal? 如果您在数据层工作,使用 java.sql.Date 应该没有问题。在任何其他层,坚持使用 java.util.Date。 【参考方案1】:tl;博士
我应该使用 java.util.Date 还是 java.sql.Date?
都没有。
从JDBC 4.2 及更高版本开始,两者都已过时。请改用 java.time 类。
仅日期值对于类似于 SQL 标准DATE
的数据库类型,请使用 java.time.LocalDate
。
LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;
myPreparedStatement.setObject( ld , … ) ;
以 UTC 值表示的日期对于类似于 SQL 标准 TIMESTAMP WITH TIME ZONE
的数据库类型,请使用 java.time.Instant
。
Instant instant = myResultSet.getObject( … , Instant.class ) ;
myPreparedStatement.setObject( instant , … ) ;
详情
问题和其他答案似乎过度思考了这个问题。 java.sql.Date 只是一个java.util.Date,其时间设置为00:00:00
。
来自the java.sql.Date doc(斜体字是我的)……
上课日期
java.lang.Object
java.util.Date ← 继承自 j.u.Date
java.sql.Date
…
一个毫秒值的瘦包装器,允许 JDBC 将其识别为 SQL DATE 值。毫秒值表示自 1970 年 1 月 1 日 00:00:00.000 GMT 以来经过的毫秒数。 ← 时间设置为零,格林威治标准时间/世界标准时间午夜
为了符合 SQL DATE 的定义,由 java.sql.Date 实例包装的毫秒值必须通过在特定时区中将小时、分钟、秒和毫秒设置为零来进行“规范化”。实例已关联。
仅日期与日期时间
核心问题是:
SQL在 SQL 中,DATE
数据类型仅存储日期,没有时间。
JAVA在与早期 Java 版本捆绑在一起的设计糟糕的日期时间库中,它们未能包含一个表示仅日期的类。
Java 团队没有创建一个仅包含日期的类,而是做了一个可怕的 hack。他们使用他们的日期时间类(错误命名的java.util.Date
类,包含日期和时间)并将其扩展为让实例将其时间设置为午夜UTC、@987654394 @。那个hack,j.u.Date的那个子类,是java.sql.Date
。
所有这些黑客行为、糟糕的设计和错误的命名都造成了混乱。
使用哪个
那么什么时候用哪个?简单,在切断混乱之后。
在读取或写入数据库的仅日期列时,请使用java.sql.Date
,因为它笨拙地试图掩盖其时间。
在 Java 中需要时间和日期的任何其他地方,请使用java.util.Date
。
当您手头有 java.sql.Date 但需要 java.util.Date 时,只需传递 java.sql.Date。作为一个子类,java.sql.Date是java.util.Date。
更好
在现代 Java 中,您现在可以选择体面的日期时间库来替代与 Java 捆绑在一起的旧的、臭名昭著的麻烦 java.util.Date、Calendar、SimpleTextFormat 和 java.sql.Date 类。主要选择有:
Joda-Time java.time(受Joda-Time启发,由JSR 310定义,与Java 8捆绑,由ThreeTen-Extra项目扩展)两者都提供LocalDate
类来仅表示日期,没有时间和时区。
更新到 JDBC 4.2 或更高版本的JDBC driver 可用于直接与数据库交换 java.time 对象。这样我们就可以彻底抛弃 java.util.* 和 java.sql.* 包中的日期时间类这种丑陋的烂摊子了。
设置对象 |获取对象
Oracle 发布的This article 解释说,如果您调用getObject
和setObject
方法,Java 8 中的 JDBC 已经透明地更新,以将 SQL DATE
值映射到新的 java.time.LocalDate 类型。
在钝化的语言中,JDBC 4.2 update spec 的底部确认了该文章,并在getObject
和setObject
方法中添加了新的映射。
myPreparedStatement.setObject( … , myLocalDate ) ;
……和……
LocalDate myLocalDate = myResultSet.getObject( … , LocalDate.class ) ;
转换
规范还说,java.sql.Date 类中添加了新方法,用于来回转换为 java.time.LocalDate。
public java.time.instant toInstant()
public java.time.LocalDate toLocalDate()
public static java.sql.Date valueOf(java.time.LocalDate)
时区
旧的java.util.Date
、java.sql.Date
和java.sql.Timestamp
始终位于UTC。前两个(至少)有一个深埋在源代码中的时区,但仅在表面下使用,例如 equals
方法,并且没有 getter/setter。
更令人困惑的是,他们的 toString
方法应用了 JVM 当前的默认时区。所以对于天真的程序员来说,似乎他们有一个时区,但他们没有。
隐藏时区和toString
行为是避免这些麻烦的旧遗留类的众多原因中的两个。
使用java.time(Java 8 及更高版本)编写您的业务逻辑。 java.time 缺少的地方,使用Joda-Time。 java.time 和 Joda-Time 都有方便的方法在需要的地方来回切换旧类。
替换:
java.util.Date
替换为 java.time.Instant
java.sql.Timestamp
替换为 java.time.Instant
java.sql.Date
替换为 java.time.LocalDate
。
java.sql.Time
替换为 java.time.LocalTime
。
Instant
类表示UTC 时间线上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。
所有三个java.time.Local…
类都缺少time zone 或offset-from-UTC 的任何概念。
关于java.time
java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date
、Calendar
和 SimpleDateFormat
。
Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。
要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。
您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.*
类。
从哪里获得 java.time 类?
Java SE 8、Java SE 9、Java SE 10 及更高版本 内置。 标准 Java API 的一部分,带有捆绑实现。 Java 9 添加了一些小功能和修复。 Java SE 6 和 Java SE 7 ThreeTen-Backport 中的大部分 java.time 功能都向后移植到 Java 6 和 7。 Android java.time 类的 android 捆绑包实现的更高版本。 对于早期的 Android (ThreeTenABP 项目适应 ThreeTen-Backport(如上所述)。见How to use ThreeTenABP…。ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如Interval
、YearWeek
、YearQuarter
和more。
【讨论】:
最后我使用了更简单更简单的 Joda-Time 库。谢谢 我不确定instance set its time-of-day to midnight UTC
.至少使用 EclipseLink JPA 实现,实例将根据本地 JVM 时区设置其时间。所以如果你将数据库中的11/5/2015
读入java.sql.Date
实例,然后在这个实例上应用getTime
,你会在不同的时区得到不同的结果。
@gamliela 阅读新增的“时区”部分。我怀疑您对 Java 的旧日期时间类中尴尬的时区行为感到困惑。另外,请阅读上面我的答案中新引用的文档:java.sql.Date 确实只是一个 java.util.Date,其时间设置为零(午夜 UTC/GMT)。
如您所说,时区内置在源代码中,只要您避免java.util.date
的弃用功能,它不应该引起任何问题 - 我同意。但我认为某些 JPA/JDBC 实现实际上使用了这些已弃用的函数,这就是您可能会遇到问题的地方。请参阅讨论here,这是一个真正的问题。底线是,如果您使用java.sql.date
,请确保您不应用getTime()
,或将您的JVM 时区设置为UTC。
关于 Java 7 和 JDBC,请重新阅读我上面的最后一条评论。我编辑了我的答案以发布相同的信息。【参考方案2】:
好吧,根据这个article,您可以在没有@Temporal
注释的情况下使用javax.sql.Date
,这可以节省您的一些编码。但是java.util.Date
更易于在整个应用程序中使用。
所以我会用
@Column(name = "date")
@Temporal(TemporalType.DATE)
private java.util.Date date;
【讨论】:
嗯,是的......但它是“java.sql.Date”和“java.sql.*”。它不是 javax 包 谢谢,我修好了。 @PetrMensik 不正确,您确实不必必须将 java.sql.Date 对象转换为 java.util.Date。 java.sql.Date 是 java.util.Date,因此您可以在任何需要 java.util.Date 的地方简单地传递一个。 请注意,即使您将该字段声明为java.util.Date
,您也可能会得到一个java.sql.Date
值。至少在 Hibernate 5 + PostgreSQL 9 中肯定会发生这种情况。因此,您不妨将该字段声明为java.sql.Date
... 在两种情况下,您最终都会得到相同的实际值,并且至少在有人阅读代码时会更加明显。【参考方案3】:
一般来说,我认为使用java.util.Date
是明智的,因为您可以在程序中的任何位置使用它,而无需转换类型或使用特定于 SQL 的代码污染您的应用程序。
我不知道“java.sql.Date”更适合的情况。
【讨论】:
java.sql.Date
将时间修剪为午夜 00:00:00,因为 JDBC 日期仅存储日期(没有时间)。如果您尝试将经过修剪的时间 java.sql.Date
与尚未修剪时间的 java.util.Date
进行比较,equals()
将返回 false。不幸的是,Oracle 日期的小数部分包括时间。对于 Oracle,java.sql.Timestamp
更合适。【参考方案4】:
根据 Java 文档,建议根据底层数据库使用适当的 Date 类型。 但是,Java 8 提供了一组丰富的 java.time 包下的类,如果应用程序是使用 Java 8 编写的,则必须使用它。
类 javaxjava.sql.Date 扩展了 java.util.Date,对毫秒容器进行了微小的更改,以便它可以有效地支持数据库 DATE 类型。这样,我们就可以保存实体类中的@Temporal注解类型了。
然而,java.util.Date 可用于在整个应用程序中获得更好的可扩展性,以便可以轻松地使用它来存储时间和日期。
【讨论】:
以上是关于在 java.util.Date 或 java.sql.Date 之间进行选择的主要内容,如果未能解决你的问题,请参考以下文章
我应该使用 java.util.Date 还是切换到 java.time.LocalDate
java.util.Date 和 java.sql.Date 有啥区别? [复制]