java.util.Date与java.sql.Date


Answers:


585

恭喜,您已经通过JDBC达到了我最喜欢的功能:Date类处理。

基本上,数据库通常至少支持三种形式的日期时间字段,即日期,时间和时间戳。它们中的每一个在JDBC中都有一个对应的类,并且它们各自都进行扩展java.util.Date。这三个中的每一个的快速语义如下:

  • java.sql.Date对应于SQL DATE,这表示它存储年,月和日,时,分,秒和毫秒被忽略。此外,sql.Date它与时区无关。
  • java.sql.Time对应于SQL TIME,并且应该很明显,它仅包含有关小时,分钟,秒和毫秒的信息
  • java.sql.Timestamp对应于SQL TIMESTAMP,它是可自定义精度的精确日期,以纳秒为单位请注意,util.Date仅支持毫秒!)。

在将JDBC驱动程序用于这三种类型时,最常见的错误之一是对这些类型的处理不正确。这意味着sql.Date特定于时区,sql.Time包含当前的年,月和日等。

最后:使用哪个?

确实取决于字段的SQL类型。PreparedStatement对所有三个值都有设置器,#setDate()分别是for sql.Date#setTime()for sql.Time#setTimestamp()for sql.Timestamp

请注意,如果使用,ps.setObject(fieldIndex, utilDateObject);您实际上可以util.Date为大多数JDBC驱动程序提供一个普通的驱动程序,它会很高兴地吞噬它,就好像它是正确的类型一样,但是当您随后请求数据时,您可能会注意到实际上丢失了一些东西。

我真的是说根本不应该使用任何日期。

我的意思是将毫秒/纳秒保存为普通的long,并将其转换为您正在使用的任何对象(强制性joda-time插件)。一种可行的方法是将日期部分存储为一个长期时间部分,将日期部分存储为另一个时间部分,例如现在将存储为20100221和154536123。这些不可思议的数字可以在SQL查询中使用,并且可以从数据库移植到另一个数据库中,并且将使您完全避免使用JDBC / Java Date API:s的这一部分。


17
好答案。但是,对于DBA而言,存储日期是否长久不就不友好了吗?
cherouvim,2010年

26
也许吧,但是,DBA:s通常倾向于选择他们的RDBMS,而直接拒绝与RDBMS无关的一切(我在看您,Oracle爱好者),而Java应用程序有望与它们一起使用。我个人根本不喜欢将逻辑放入数据库。
艾斯科(Esko)2010年

2
我的mysql列是一个日期时间,但是在做ps.setDate(new java.sql.Date(myObject.getCreatedDate()。getTime())); 我失去了毫秒部分,该如何解决?
Blankman

2
为了不浪费毫秒:new java.sql.Timestamp(utilDate.getTime())
Kieveli,2012年

3
我提到过,这是一个常见的错误,它是TZ特定的,而按规范它不应该。
埃斯科

60

最新编辑:从Java 8开始,您既不应使用也java.util.Date不要 java.sql.Date避免使用它,而应该使用java.time软件包(基于Joda)而不是其他任何东西。如果您使用的不是Java 8,则原始答复如下:


java.sql.Date-调用使用它的库的方法/构造函数时(例如JDBC)。并非如此。您不想为未明确处理JDBC的应用程序/模块的数据库库引入依赖关系。

java.util.Date-使用使用它的库时。否则,出于以下几个原因,应尽可能少:

  • 它是可变的,这意味着每次将其传递给方法或从方法返回时,都必须对其进行防御性复制。

  • 它不能很好地处理日期,这确实使像您这样的人倒退,认为日期处理类应该如此。

  • 现在,由于juD不能很好地完成工作,Calendar因此引入了可怕的类。它们也是易变的,并且很难使用,如果您别无选择,则应避免使用它们。

  • 还有更好的替代方法,例如Joda Time API甚至可以将其纳入Java 7并成为新的官方日期处理API -a快速搜索说它不会)。

如果您觉得引入像Joda这样的新依赖关系是过大的,则将longs用于对象中的时间戳字段并不是很糟糕,尽管我本人通常在传递它们时将它们包装在juD中,以用于类型安全和用作文档。


5
当存储在数据库中时,为什么我们比java.sql更喜欢java.time?我在发言真的很有趣,但我想知道为什么:)
让·弗朗索瓦·Savard

@让FrançoisSavard我希望你已经发现你的问题的答案,因为你张贴评论-但这里有一个答案,只是为了完整起见:它仍然是非常确定,java.sql.DatePreparedStatement等!但是,当您将其传递时,请使用LocalDate转换时使用java.sql.Date.valueOfjava.sql.Date.valueOf进行设置时,然后java.sql.Date.toLocalDate 再次使用- 尽快将其转换回原来的位置,因为您希望尽可能少地涉及java.sql,并且它是可变的。
gustafc

19

唯一可以使用的时间java.sql.DatePreparedStatement.setDate。否则,请使用java.util.Date。它告诉您ResultSet.getDate返回a,java.sql.Date但可以将其直接分配给a java.util.Date


3
嗯,ResultSet#getDate()返回sql.Date(扩展了util.Date)。
艾斯科(Esko)2010年

3
@Esko-“嗯”,在您发表评论(并投票否决)之前,我已将其修复。
Paul Tomblin'2

1
重要的是要注意,之所以可以将java.sql.Date分配给java.util.Date是因为第一个是第二个的子类。
dj18 2012年

2
当前者扩展后者时,为什么要“说”一个java.sql.Datea java.util.Date?您要说明的重点是什么?
洛恩侯爵,

11

我遇到了同样的问题,我发现将当前日期插入准备好的语句中的最简单方法是:

preparedStatement.setDate(1, new java.sql.Date(new java.util.Date().getTime()));

2
无法获得时区。
erhanasikoglu,2015年

4

tl; dr

都不使用。

都没有

java.util.Date与java.sql.Date:何时使用哪个以及为什么使用?

这两个类都很糟糕,在设计和实现上都有缺陷。避免像瘟疫 冠状病毒

而是使用JSR 310中定义的java.time类。这些类是用于处理日期时间处理的行业领先的框架。这些完全取代血腥可怕的遗产类,如DateCalendarSimpleDateFormat,和这样的。

java.util.Date

第一个java.util.Date表示以UTC表示的时刻,表示相对于UTC的零时分分钟秒。

java.time.Instant

现在由取代java.time.Instant

Instant instant = Instant.now() ;  // Capture the current moment as seen in UTC.

java.time.OffsetDateTime

Instantjava.time的基本构建块类。为了获得更大的灵活性,OffsetDateTime请将set设置ZoneOffset.UTC用于相同的目的:以UTC表示时刻。

OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ;

您可以通过使用这个对象发送到数据库PreparedStatement::setObjectJDBC 4.2或更高版本。

myPreparedStatement.setObject(  , odt ) ;

检索。

OffsetDateTime odt = myResultSet.getObject(  , OffsetDateTime.class ) ;

java.sql.Date

这个java.sql.Date班也很糟糕和过时了。

此类仅表示日期,没有日期和时区。不幸的是,在糟糕的设计中,此类继承了该类,该类java.util.Date代表了一个时刻(UTC中带有日期的日期)。因此,该类仅假装为仅日期,而实际上却带有UTC的时间和隐式偏移量。这引起了很多混乱。永远不要使用此类。

java.time.LocalDate

相反,可使用java.time.LocalDate它仅跟踪日期(年,月,日),而没有任何时间,任何时区或偏移量。

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
LocalDate ld = LocalDate.now( z ) ;    // Capture the current date as seen in the wall-clock time used by the people of a particular region (a time zone).

发送到数据库。

myPreparedStatement.setObject(  , ld ) ;

检索。

LocalDate ld = myResultSet.getObject(  , LocalDate.class ) ;

Java(旧式和现代)和标准SQL中的日期时间类型表


关于java.time

java.time框架是建立在Java 8和更高版本。这些类取代麻烦的老传统日期时间类,如java.util.DateCalendar,和SimpleDateFormat

要了解更多信息,请参见Oracle教程。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

现在处于维护模式Joda-Time项目建议迁移到java.time类。

您可以直接与数据库交换java.time对象。使用与JDBC 4.2或更高版本兼容的JDBC驱动程序。不需要字符串,也不需要java.sql.*类。

在哪里获取java.time类?

与哪个版本的Java或Android一起使用的哪个java.time库的表


1
支持用电晕病毒代替鼠疫参考。现在更相关。
ankuranurag2

2

Java中的java.util.Date类表示特定的时间点(例如,2013年11月25日16:30:45降至毫秒),但是数据库中的DATE数据类型仅表示日期(例如, 2013年11月25日)。为了防止错误地向数据库提供java.util.Date对象,Java不允许您直接将SQL参数设置为java.util.Date:

PreparedStatement st = ...
java.util.Date d = ...
st.setDate(1, d); //will not work

但是它仍然允许您通过强制/意图来执行此操作(然后DB驱动程序将忽略小时和分钟)。这是通过java.sql.Date类完成的:

PreparedStatement st = ...
java.util.Date d = ...
st.setDate(1, new java.sql.Date(d.getTime())); //will work

一个java.sql.Date对象可以存储时间(这样很容易从java.util.Date构造),但是如果您试图要求小时,则会抛出一个异常(以加强其作为对象的概念)。仅日期)。希望数据库驱动程序可以识别此类,并在小时内仅使用0。尝试这个:

public static void main(String[] args) {
  java.util.Date d1 = new java.util.Date(12345);//ms since 1970 Jan 1 midnight
  java.sql.Date d2 = new java.sql.Date(12345);
  System.out.println(d1.getHours());
  System.out.println(d2.getHours());
}

0

java.util.Date代表毫秒内的特定时间点。它代表没有时区的日期和时间信息。java.util.Date类实现Serializable,Cloneable和Comparable接口。它是由遗传java.sql.Datejava.sql.Timejava.sql.Timestamp接口。

java.sql.Date扩展了java.util.Date类,该类表示没有时间信息的日期,并且仅在处理数据库时才应使用。为了符合SQL DATE的定义,java.sql.Date必须通过将与实例相关联的特定时区中的小时,分​​钟,秒和毫秒设置为零来“标准化”实例包装的毫秒值。

它继承了所有公共方法java.util.Date,如getHours()getMinutes()getSeconds()setHours()setMinutes()setSeconds()。由于java.sql.Date不存储时间信息,因此从它们的实现详细信息中可以明显看出,它会覆盖来自的所有时间操作,java.util.Date并且java.lang.IllegalArgumentException如果调用这些方法,则会抛出所有这些方法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.