我应该使用java.util.Date还是java.sql.Date?
我有一个VisualFox数据库,我使用适当的jdbc类型4驱动程序使用IntelliJ Idea向导检索实体。
ide(或驱动程序)已将日期字段创建为Timestamp。 但是,日期字段不是时间戳,而是日期字段,它们仅存储年,月和日。
所以我想知道是否应该切换到java.util.Date或java.sql.Date。 乍一看,我认为java.sql.Date应该是适当的,但它有许多声明为弃用的方法。
-
你使用的JPA映射是? 以@Temporal为例?
-
如果您在数据层工作,使用java.sql.Date应该没有问题。 在任何其他层中,坚持使用java.util.Date。
TL;博士
Should I use java.util.Date or java.sql.Date?
Ok.
都不是。
从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。
<5233>
myPreparedStatement.setObject( instant , … ) ;
细节
问题和其他答案似乎过分考虑了这个问题。 java.sql.Date只是一个java.util.Date,其时间设置为00:00:00。
从java.sql.Date doc(斜体文本是我的)...
Class Date
Ok.
java.lang.Object
Ok.
java.util.Date ← Inherits from j.u.Date
Ok.
java.sql.Date
Ok.
…
Ok.
A thin wrapper around a millisecond value that allows JDBC to identify this as an SQL DATE value. A milliseconds value represents the number of milliseconds that have passed since January 1, 1970 00:00:00.000 GMT. ← Time-of-day set to Zero, midnight GMT/UTC
Ok.
To conform with the definition of SQL DATE, the millisecond values wrapped by a java.sql.Date instance must be 'normalized' by setting the hours, minutes, seconds, and milliseconds to zero in the particular time zone with which the instance is associated.
Ok.
仅日期与日期时间
核心问题是:
SQL
在SQL中,DATE数据类型仅存储日期,而不存在时间。
JAVA
在与Java早期版本捆绑在一起的设计糟糕的日期时间库中,它们未能包含一个表示仅限日期的类。
Java团队没有创建一个仅限日期的类,而是制造了一个糟糕的黑客。他们采用了日期时间类(名称错误的java.util.Date类,包含日期和时间)并将其扩展为将实例设置为时间到午夜UTC,00:00:00。那个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类。主要选择是:
乔达时间
java.time
(受Joda-Time启发,由JSR 310定义,与Java 8捆绑在一起,由ThreeTen-Extra项目扩展)
两者都提供LocalDate类来表示日期,没有时间和没有时区。
更新到JDBC 4.2或更高版本的JDBC驱动程序可用于直接与数据库交换java.time对象。然后我们可以完全放弃丑陋的混乱,它是java.util。*和java.sql。*包中的日期时间类。
setObject |的getObject
Oracle发布的这篇文章解释了如果调用getObject和setObject方法,Java 8中的JDBC已经透明地更新,以将SQL DATE值映射到新的java.time.LocalDate类型。
在钝语言中,JDBC 4.2更新规范的底部确认了该文章,并将新映射添加到getObject和setObject方法中。
1
| myPreparedStatement.setObject( … , myLocalDate ) ; |
…和…
1
| LocalDate myLocalDate = myResultSet.getObject( … , LocalDate.class ) ; |
兑换
该规范还说java.sql.Date类中添加了新方法,可以来回转换为java.time.LocalDate。
<5233>
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时间轴上的一个时刻,分辨率为纳秒(小数部分最多九(9)位)。
所有三个java.time.Local…类都缺少任何时区概念或从UTC偏移。
关于java.time
java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧遗留日期时间类,例如java.util.Date,Calendar和&amp; SimpleDateFormat。
现在处于维护模式的Joda-Time项目建议迁移到java.time类。
要了解更多信息,请参阅Oracle教程。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。
您可以直接与数据库交换java.time对象。使用符合JDBC 4.2或更高版本的JDBC驱动程序。不需要字符串,不需要java.sql.*类。
从哪里获取java.time类?
Java SE 8,Java SE 9,Java SE 10及更高版本
内置。
带有捆绑实现的标准Java API的一部分。
Java 9增加了一些小功能和修复。
Java SE 6和Java SE 7
许多java.time功能都被反向移植到Java 6&amp; 7在ThreeTen-Backport。
Android的
更高版本的Android捆绑java.time类的实现。
对于早期的Android(<26),ThreeTenABP项目采用ThreeTen-Backport(如上所述)。请参见如何使用ThreeTenABP ....
ThreeTen-Extra项目使用其他类扩展了java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的类,例如Interval,YearWeek,YearQuarter等。
好。
-
最后我使用了更简单,更简单的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实现实际上使用了这些已弃用的函数,而这正是您可能遇到问题的地方。看到这里的讨论,这是一个真正的问题。最重要的是,如果您使用java.sql.Date,请确保不应用getTime(),或将JVM时区设置为UTC。
-
我将基本上重复@gamliela所说的内容,并建议相应地澄清你的答案。当使用UTC呈现时,JDBC不会创建java.sql.Date,其中时间部分为00:00。相反,它在JVM的默认时区(或者,JDBC连接的AFAIR,如果您想要从默认时间更改其时区)中呈现时创建一个时间部分为00:00的位置。 java.sql.Time遵循相同的逻辑;如果使用JVM的默认时区渲染它,它看起来像预期的那样。
-
我也没有关注"旧的日期时间课程"。 java.util.Date不需要时区,因为它是"物理"时间,与您所处的位置无关。因此,它可以在任何时区中呈现,并且在每个时区中看起来都不同。它的toString没有任何根本性的错误(虽然带有区域偏移的ISO日期时间可能会更有帮助)。问题是数据库中的日期和时间值通常只是一堆字段(Y,M,D等),无法在"物理"时间轴上转换为瞬间,因此JDBC与JVM的解决方法时区。
-
@ddekany不,你错了:java.sql.Date确实是GMT(实际上是UTC)。它在我引用的文档中说的正确!我建议你仔细阅读我的答案。 java.sql.Date在技术上是java.util.Date的子类,尽管文档建议我们假装否则。 JVM的当前默认时区与java.sql.Date的值无关。所有这些都指向早期Java团队的善意但不明智的设计决策。值得庆幸的是,我们现在拥有java.time类,可以忘记这些麻烦的旧遗留类。
-
@ddekany关于你的引用:"它是"物理"时间,与你所处的位置无关"。我不知道你想表达什么意思。哪有这回事。我们只能通过引用来测量时间,而现在的参考通常是UTC。 A java.util.Date跟踪时间,作为UTC中1970年第一时刻的毫秒数。
-
@ddekany RE你的引用:"数据库中的日期和时间值通常只是一堆字段(Y,M,D等),无法在"物理"时间线上转换为瞬间,"。简单地说,数据库通常存储日期时间的程度如何。而且我们不能从数据库中获取时间表上的时刻 - 坦率地说,这句话和你的"JDBC解决方法"是荒谬的,或者我很想念你的观点。 myResultSet.getObject(…,Instant.class)我建议更多地研究如何完成日期时间处理。阅读更多Stack Overflow&amp;开源代码,并忽略遗留Java类。
-
@BasilBourque即使您引用的文档也说明java.sql.Date的时间部分在与实例关联的特定时区中为"零",而不是在UTC时区。但如果您仍然不相信我,请尝试自己的JDBC驱动程序。 (我已经用2年前的MySQL,Oracle和PostgreSQL对它进行了测试。我现在也用PostgreSQL对它进行了测试。)
-
@BasilBourque:我并不是说数据库不能存储时间点,但是它们通常(我会冒险,通常会冒险)存储仅限日期和仅限时间的值,如"一堆字段"。 (特别是,仅日期值通常没有"带时区"变体。)无论如何,在将这些值作为Date -s返回时,必须为此做好准备。我不确定"JDBC中的解决方法"是什么荒谬的......它只是它的工作原理。
-
日历非常有时间区
-
@KalpeshSoni ??
-
如果你有java 8你可以使用java.time,在大多数情况下该日历工作之前 - 你说他们没有时区!
-
我知道它很复杂,当你考虑Oracle的行为时更是如此,但是 - stackoverflow.com/questions/14070572/…
-
@KalpeshSoni你能指出我说的java.util.Calendar类没有时区吗?要么你误解了我,要么我误传了。除此之外,没有理由再次使用Date,Calendar或Timestamp类。现在完全取代了Java 8及更高版本中内置的java.time类。对于Java 6&amp; 7,使用ThreeTen-Backport项目,其中大部分java.time功能已被反向移植。
-
Java中的旧日期时间类(java.util.Date/.'Calendar',java.sql.Date / .Timestamp等,在java.time之前)实际上没有时区
-
如果你正在处理java 7和jdbc,你没有使用joda时间或java 8 apis的奢侈
-
至于Java 7和JDBC,请重新阅读上面的上一条评论。我编辑了我的答案以发布相同的信息。
-
@KalpeshSoni至于Calendar和时区,我的写作不正确。固定。谢谢。
-
非常感谢您的更新
那么,根据这篇文章你可以使用javax.sql.Date而不用@Temporal注释,这可以节省一些编码。但是java.util.Date更易于在整个应用程序中使用。
所以我会用
1 2 3
| @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 ......在这两种情况下你最终得到相同的实际值,至少当有人读取代码时它更明显。
通常,我发现建议使用java.util.Date,因为您可以在程序中的任何位置使用它,而无需转换类型或使用特定于SQL的代码污染应用程序。
我不知道'java.sql.Date'会更适合的情况。
-
java.sql.Date将时间减少到午夜00:00:00,因为JDBC日期只存储日期(没有时间)。如果您尝试将java.sql.Date与修剪到java.util.Date的时间进行比较,其中java.util.Date尚未修剪时间,equals()将返回false。不幸的是,Oracle日期包括其小数部分的时间。对于Oracle,java.sql.Timestamp更合适。
根据Java doc,建议根据底层数据库使用适当的Date类型。
但是,使用Java 8,已经提供了java.time包下的一组丰富的类,如果应用程序是用Java 8编写的,则必须使用它。
类javaxjava.sql.Date扩展了java.util.Date,对miliseconds容器进行了少量更改,以便它可以有效地支持Database DATE类型。这样,我们可以保存从实体类输入的@Temporal注释。
但是,java.util.Date可用于在整个应用程序中实现更好的可伸缩性,以便它可以轻松地用于存储时间和日期。