【问题标题】:java.util.Date vs java.sql.Datejava.util.Date 与 java.sql.Date
【发布时间】:2011-01-19 08:14:30
【问题描述】:

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

【问题讨论】:

    标签: java sql datetime date jdbc


    【解决方案1】:

    tl;博士

    两者都不使用。

    都没有

    java.util.Date vs 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

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


    关于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 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

    从哪里获得 java.time 类?

    【讨论】:

    • 赞成用冠状病毒替换瘟疫参考。现在更相关了。
    • 嗨@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.DateLocalDate,依此类推。
    【解决方案2】:

    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

    【讨论】:

      【解决方案3】:

      使用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?你想表达什么意思?
      【解决方案4】:

      后期编辑:从 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 这样的新依赖有点过头了,longs 用于对象中的时间戳字段并不是那么糟糕,尽管我自己通常在传递它们时将它们包装在 juD 中,以确保类型安全并作为文档。

      【讨论】:

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

      恭喜,你用 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,它们会愉快地吞噬它,就好像它是正确的类型一样,但是当您之后请求数据时,您可能会注意到你实际上缺少一些东西。

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

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

      【讨论】:

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

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

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

      【讨论】:

      • 无法通过这个获取时区。
      【解决方案7】:

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

      【讨论】:

        猜你喜欢
        • 2011-08-29
        • 2016-02-14
        • 2023-03-13
        • 2015-08-09
        • 1970-01-01
        • 1970-01-01
        • 2015-04-03
        相关资源
        最近更新 更多