ChatGPT解决这个技术问题 Extra ChatGPT

java.util.Date 与 java.sql.Date

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


i
indivisible

恭喜,你用 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 是特定于时区的,sql.Time 包含当前年份、月份和天等等等等。

最后:用哪一个?

取决于字段的 SQL 类型,真的。 PreparedStatement 具有所有三个值的设置器,#setDate()sql.Date 的设置器,#setTime()sql.Time 的设置器,#setTimestamp()sql.Timestamp 的设置器。

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

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

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


不错的答案。但是存储日期是否对 DBA 有点不友好?
也许,但是 DBA:s 通常倾向于他们选择的 RDBMS,并直接拒绝与该 RDBMS 无关的所有内容(我在看你,Oracle 粉丝),而 Java 应用程序应该与所有这些都一起工作。就个人而言,我根本不喜欢将我的逻辑放入数据库中。
我的 mysql 列是一个日期时间,但是在做 ps.setDate(new java.sql.Date(myObject.getCreatedDate().getTime()));我失去了毫秒部分,如何解决这个问题?
为了不丢失毫秒: new java.sql.Timestamp( utilDate.getTime() )
我提到这是一个常见的错误,它是 TZ 特定的,而规范不应该是。
C
Community

后期编辑:从 Java 8 开始,如果可以避免的话,您不应该使用 java.util.Datejava.sql.Date,而是更喜欢使用 java.time 包(基于 Joda)而不是任何东西别的。如果您不在 Java 8 上,以下是原始响应:

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

java.util.Date - 使用使用它的库时。否则,尽可能少,原因如下:

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

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

现在,因为 juD 的工作做得不好,所以引入了可怕的 Calendar 类。它们也是可变的,使用起来很糟糕,如果你别无选择,应该避免使用它们。

还有更好的选择,比如 Joda Time API(它甚至可能进入 Java 7 并成为新的官方日期处理 API - 快速搜索表明它不会)。

如果您觉得引入像 Joda 这样的新依赖项有些矫枉过正,那么将 long 用于对象中的时间戳字段并不是那么糟糕,尽管我自己通常在传递它们时将它们包装在 juD 中,以确保类型安全和作为文档.


在数据库中存储时,为什么我们更喜欢 java.time 而不是 java.sql?我对声明很感兴趣,但我想了解原因:)
@Jean-FrançoisSavard 我希望自从您发布该评论以来您已经找到了问题的答案 - 但这里有一个答案,只是为了完整起见:java.sql.DatePreparedStatement 等仍然完全可以!但是,当您传递它时,请使用 LocalDate,您在设置时使用 java.sql.Date.valueOfjava.sql.Date.valueOf 转换它,并使用 java.sql.Date.toLocalDate 尽早将其转换回来 - 再次,因为您想涉及 java .sql 尽可能少,因为它是可变的。
s
saravanakumar

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


嗯,ResultSet#getDate() 返回 sql.Date(它扩展了 util.Date)。
@Esko - “Ehm”,我在你评论(和否决)之前修复了这个问题。
重要的是要注意 java.sql.Date 可以分配给 java.util.Date 的原因是因为第一个是第二个的子类。
当前者扩展后者时,为什么“告诉”可以将 java.sql.Date 分配给 java.util.Date?你想表达什么意思?
B
Basil Bourque

tl;博士

两者都不使用。

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

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

两者都不

java.util.Date vs java.sql.Date:什么时候用哪个,为什么用?

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

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

java.util.日期

第一个 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 设置为 ZoneOffset.UTC 用于相同目的:表示 UTC 中的时刻。

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

您可以使用带有 JDBC 4.2 或更高版本的 PreparedStatement::setObject 将此对象发送到数据库。

myPreparedStatement.setObject( … , odt ) ;

取回。

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

java.sql.日期

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 ) ;

https://i.stack.imgur.com/UmyHG.png

关于 java.time

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

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

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

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

从哪里获得 java.time 类?

Java SE 8、Java SE 9、Java SE 10、Java SE 11 及更高版本 - 标准 Java API 的一部分,具有捆绑的实现。 Java 9 添加了一些小功能和修复。

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

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

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

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

java.time 类的更高版本的 Android 捆绑实现。

对于早期的 Android(<26),ThreeTenABP 项目采用了 ThreeTen-Backport(如上所述)。请参阅如何使用 ThreeTenABP……。

https://i.stack.imgur.com/ImlbG.png


赞成用冠状病毒替换瘟疫参考。现在更贴切了。
嗨@Basil。我必须使用 IBM 的 ICU(“必须”:据我所知),因为我使用的是伊朗日历(有人说是 Jalali 日历)。 IBM 的 ICU 使用臭名昭著的(可能是强词)java.util.Date api。在阅读了您的详细答案后,我决定使用 java.time 但我必须将它们全部转换。只是几节课。我使用 java.time.Instant 编写了一个转换器。我的问题有没有好的解决方案(如果这是一个问题。实际上我需要你的指导;也许我不知道问题出在哪里)?
我差点忘了谢谢你的回答。谢谢你。
@Arash 至于遗留日期时间类和 java.time 之间的转换,是的,这很容易和干净。寻找添加到旧类的新转换方法。查找 to…/from… 方法,例如 java.util.Date.from( Instant )java.util.Date 映射到 InstantGregorianCalendar 映射到 ZonedDateTimejava.sql.Date 映射到 LocalDate,依此类推。
I
Israelm

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

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

不能用这个得到时区。
K
Kent Tong

Java 中的 java.util.Date 类表示特定的时间点(例如,2013 年 11 月 25 日 16:30:45 到毫秒),但 DB 中的 DATE 数据类型仅表示日期(例如, 2013 年 11 月 25 日)。为了防止您错误地将 java.util.Date 对象提供给 DB,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 构造),但是如果您尝试向它询问小时数(以强制执行其作为仅限日期)。 DB 驱动程序应该能够识别这个类,并且只使用 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());
}

A
Abdul Alim Shakir

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,从它们的实现细节中可以明显看出。