java.util.Date vs java.sql.Date

时间:2010-02-21 13:16:40

标签: java sql datetime date jdbc

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

7 个答案:

答案 0 :(得分:563)

恭喜,您已经使用JDBC点击了我最喜欢的宠物:日期类处理。

基本上,数据库通常至少支持三种形式的日期时间字段,即日期,时间和时间戳。其中每个都有一个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是特定于时区的,{{1}包含当前年,月,日等等。

最后:使用哪一个?

确实取决于该字段的SQL类型。 sql.Time包含所有三个值的设置者,PreparedStatement#setDate()sql.Date#setTime()sql.Time#setTimestamp()

请注意,如果您使用sql.Timestamp,您实际上可以为大多数JDBC驱动程序提供正常的ps.setObject(fieldIndex, utilDateObject);,这些驱动程序会愉快地吞噬它,就像它的类型正确一样,但是当您之后请求数据时,你可能会注意到你实际上缺少了东西。

我真的说没有任何日期应该被使用。

我所说的是将毫秒/纳秒保存为普通长并将它们转换为您正在使用的任何对象( obligatory joda-time plug )。可以做的一种hacky方法是将日期组件存储为另一个长和时间组件,例如现在将是20100221和154536123.这些幻数可以在SQL查询中使用,并且可以从数据库移植到另一个和将让你完全避免使用JDBC / Java Date API的这一部分。

答案 1 :(得分:57)

LATE EDIT:从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并成为新的官方日期处理API - 一个quick search说它不会。)

如果你认为引入一个像Joda这样的新依赖是不合适的,long对于对象中的时间戳字段来说并不是那么糟糕,尽管我自己通常在传递它们时将它们包装在juD中,因为类型安全和文档。

答案 2 :(得分:19)

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

答案 3 :(得分:10)

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

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

答案 4 :(得分:7)

tl; dr

都不使用。

都不是

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

这两个类都很糟糕,在设计和实现上都有缺陷。避免像Plague Coronavirus那样。

请使用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

Instant java.time 的基本构建模块类。为了获得更大的灵活性,请将OffsetDateTime设置为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 ) ;

Table of date-time types in Java (both legacy and modern) and in standard SQL


关于 java.time

java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat

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

Joda-Time项目(现在位于maintenance mode中)建议迁移到java.time类。

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

在哪里获取java.time类?

Table of which java.time library to use with which version of Java or Android

答案 5 :(得分:2)

Java中的java.util.Date类表示特定时刻(例如,2013年11月25日16:30:45到毫秒),但DB中的DATE数据类型仅表示日期(例如,2013年11月25日)。为了防止您错误地向java提供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());
}

答案 6 :(得分: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(),{{1} }。由于setSeconds()不存储时间信息,因此它会覆盖java.sql.Date中的所有时间操作,并且如果从实现细节中显而易见,所有这些方法都将抛出java.util.Date