java.util.Datejava.sql.Date:何时以及为何使用?

#1 楼

恭喜,您最喜欢使用JDBC:Date类处理。

基本上,数据库通常至少支持三种形式的datetime字段,即date,time和timestamp。它们每个在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的SQL类型真的。 PreparedStatement具有所有三个值的设置器,#setDate()sql.Date的一个值,#setTime()sql.Time的一个值,#setTimestamp()sql.Timestamp的一个值。请注意,如果使用ps.setObject(fieldIndex, utilDateObject);,实际上可以为大多数JDBC驱动程序提供一个普通的util.Date会很高兴地将其吞噬,就好像它是正确的类型一样,但是当您随后请求数据时,您可能会注意到您实际上丢失了东西。

我真的是在说日期不应该完全可以使用。

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

评论


好答案。但是,对于DBA而言,存储时间是否长久不就不友好了吗?

–cherouvim
2010-2-21在15:22

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

– Esko
2010-2-21在17:02

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

–布兰克曼
2012年4月23日在18:32

为了不浪费毫秒:new java.sql.Timestamp(utilDate.getTime())

–基辅利
2012年8月16日在18:58

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

– Esko
13年6月13日在11:07

#2 楼

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



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

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


它是可变的,这意味着每次将其传递给方法或从方法返回时,都必须对其进行防御性复制。
它不能很好地处理日期,这使像您这样的人真正倒退,认为日期处理类应该如此。
现在,由于j.u.D不能很好地完成工作,因此引入了可怕的Calendar类。它们也是易变的,很糟糕,如果您别无选择,应该避免使用它们。
还有更好的选择,例如Joda Time API(甚至可以将其纳入Java 7并成为Java 7)。新的官方日期处理API-快速搜索表明不会)。

如果您觉得引入像Joda这样的新依赖项过于刻薄,则long并不是很容易用于时间戳字段在对象中,尽管我自己通常在将它们传递时将它们包装在juD中,以确保类型安全和用作文档。

评论


当存储在数据库中时,为什么我们比java.sql更喜欢java.time?我在声明中真的很有趣,但是我想理解为什么:)

–让·弗朗索瓦·萨瓦德(Jean-FrançoisSavard)
16-11-25在14:53



@Jean-FrançoisSavard我希望您在发表评论后找到问题的答案-但这只是出于完整性的考虑:java.sql.Date和PreparedStatement等仍然可以!但是,在传递它时,请使用LocalDate,并在设置它时使用java.sql.Date.valueOf和java.sql.Date.valueOf进行转换,并尽可能早地使用java.sql.Date将其转换回来。 toLocalDate-再次,因为您希望尽可能少地涉及java.sql,并且因为它是可变的。

–gustafc
4月7日7:30

#3 楼

唯一使用java.sql.Date的时间是在PreparedStatement.setDate中。否则,请使用java.util.Date。这说明ResultSet.getDate返回了java.sql.Date,但可以将其直接分配给java.util.Date

评论


嗯,ResultSet#getDate()返回sql.Date(扩展了util.Date)。

– Esko
2010-2-21在13:24

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

– Paul Tomblin
2010-2-21在13:26

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

– dj18
2012年6月27日13:16

当前者扩展后者时,为什么要“说”可以将java.sql.Date分配给java.util.Date?您要说明的重点是什么?

–user207421
2013年9月17日下午5:17

#4 楼

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

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


评论


无法获得时区。

–erhan asikoglu
15年1月27日在12:36

#5 楼

tl; dr

都不使用。



java.time.Instant替换java.util.Date


java.time.LocalDate替换java.sql.Date


都不是


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


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

而是使用JSR 310中定义的java.time类。这些类是用于处理日期时间处理的行业领先的框架。这些完全取代了诸如DateCalendarSimpleDateFormat之类的血腥可怕的遗留类。与UTC的时差为零时,分,秒。

java.util.Date

现在替换为java.util.Date

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


java.time.Instant

java.time.Instant是java.time的基本构建块类。为了获得更大的灵活性,出于相同的目的,请使用设置为java.time.OffsetDateTimeInstant:在UTC中表示时刻。

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


您可以通过将JDBC与OffsetDateTime一起使用,将该对象发送到数据库4.2或更高版本。

myPreparedStatement.setObject( … , odt ) ;


检索。

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


ZoneOffset.UTC

PreparedStatement::setObject类也很糟糕和过时。

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

java.sql.Date

相反,请使用java.util.Date来仅跟踪日期(年,月,日),而无需跟踪任何时间。任何时区或偏移量。

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.time


java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧式传统日期时间类,例如java.time.LocalDatejava.time.LocalDatejava.util.Date

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

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

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

从哪里获取java.time类?



Java SE 8,Java SE 9,Java SE 10,Java SE 11和更高版本-具有捆绑实现的标准Java API的一部分。


Java 9添加了一些次要功能和修复。



Java SE 6和Java SE 7


大多数java.time功能都在ThreeTen-Backport中反向移植到Java 6和7。



Android


较新版本的java.time类的Android捆绑实现。
对于较早的Android(<26),ThreeTenABP项目改编了ThreeTen-Backport(如上所述)。请参阅如何使用ThreeTenABP…。





评论


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

– ankuranurag2
5月14日14:34

#6 楼

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


但是它仍然允许您通过强制/意图进行操作(然后,小时和分钟将被数据库驱动程序忽略)。这是通过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());
}


#7 楼

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