java.util.Date
与java.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的这一部分。
#2 楼
最新编辑:从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并成为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类。这些类是用于处理日期时间处理的行业领先的框架。这些完全取代了诸如
Date
,Calendar
,SimpleDateFormat
之类的血腥可怕的遗留类。与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.OffsetDateTime
的Instant
:在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.LocalDate
,java.time.LocalDate
和java.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.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()
,setSeconds()
。由于java.sql.Date
不存储时间信息,因此它将覆盖java.util.Date
的所有时间操作,并且如果从实现细节中显而易见,则所有这些方法都将抛出java.lang.IllegalArgumentException
。
评论
好答案。但是,对于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