java.util.Date
vs java.sql.Date
:何时使用哪个以及为什么?
答案 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.Date
或java.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)
都不使用。
java.time.Instant
替换java.util.Date
java.time.LocalDate
替换java.sql.Date
java.util.Date与java.sql.Date:何时使用哪个以及为什么使用?
这两个类都很糟糕,在设计和实现上都有缺陷。避免像Plague Coronavirus那样。
请使用JSR 310中定义的 java.time 类。这些类是用于处理日期时间处理的行业领先的框架。这些完全取代了诸如Date
,Calendar
,SimpleDateFormat
之类的血腥可怕的遗留类。
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::setObject
与JDBC 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.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和SimpleDateFormat
。
要了解更多信息,请参阅Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规范为JSR 310。
Joda-Time项目(现在位于maintenance mode中)建议迁移到java.time类。
您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*
类。
在哪里获取java.time类?
答案 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.Date
,java.sql.Time
和java.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
。