Java 8中的java.sql.Date与Java 6相比如何?

5
我知道java.sql.Date应该将小时,分钟,秒和毫秒设置为零,以遵守标准SQL日期的定义。这在此处有记录(Java 8中也是如此)。
我还知道Oracle DATE类型具有年、月、日、小时、分钟和秒的时间字段。但没有小数秒和时区。
我注意到在Java 6和Java 8中同一查询的行为不同:
private static final String REQUETE_LISTE_CALENDRIER_DATE = 
    " SELECT ID_DATE, JOUR" +
    " FROM CALENDRIER " +
    " WHERE ID_DATE = ? ";

将定义如下的java.sql.Date“dateCourante”(它设置这些时间字段的值)绑定到PreparedStatement中:

GregorianCalendar gregorianCalendar = new GregorianCalendar(); // "now"
java.sql.Date dateCourante = new java.sql.Date(gregorianCalendar.getTime().getTime()); // date AND time of "now"
  • 在Java 6中,我找到一个值,
  • 在Java 8中,我没有。

在我的数据库中,日期的小时、分钟和秒数都为零。我们可以通过以下查询进行检查:

select to_char(id_date, 'DD/MM/YYYY HH24:MI:SS')
from calendrier
where id_date = to_date('26/08/2016', 'DD/MM/YYYY');

给出的信息如下:

2016年8月26日 00:00:00

据我所知,以下是Java 6和Java 8中java.sql.Date的不同之处:

  • 在Java 6中,在将查询发送到数据库之前,java.sql.Date的时间字段会被设置为零。
  • 而在Java 8中,java.sql.Date的时间字段在查询中保持不变。

我无法找到关于此行为的文档说明。

有人可以确认或解释一下吗?

作为解决方法,可以使用以下代码,如此处所述

dDate = java.sql.Date.valueOf(dDate.toLocalDate()); //其中dDate是java.sql.Date类型的


5
据我所知,这取决于Oracle JDBC驱动程序,而不是Java版本。较新的驱动程序不符合JDBC规范(因为Oracle不关心自己的规范)。参考链接:https://community.oracle.com/message/13398818#13398818 - user330315
java.sql.Date 的实现不会截断提供的毫秒值(如果我没记错的话,它从来没有这样做过);javadoc 中的文本是对驱动程序开发人员如何处理它的指导。因此问题很可能是Oracle驱动程序如何处理它。 - Mark Rotteveel
实际上,以上内容在问题引用的链接中有详细说明:“如果给定的毫秒值包含时间信息,驱动程序将设置时间组件为对应于零时区的时间[...]”。 - mustaccio
如果将a_horse_with_no_name和mustaccio的评论结合起来看,似乎可以得出一个不错的答案。 - Rodjf
2个回答

3
虽然我没有解释,但是许多人已经报告了最新一代Oracle驱动程序的问题和JDBC违规行为,我可以说您正在错误地使用这些类。而且有更好的类可用。 java.sql.Date只有日期,没有时间
在Java端,您选择了错误的类。虽然java.sql.Date类确实在其内部设置了UTC中的时间00:00:00,但是您应该忽略这个事实,正如类文档所指示的那样。 java.sql.Date旨在表示仅日期值,不带时间和时区。
这个类是一个糟糕的设计,一个糟糕的hack,继承自java.util.Date,同时告诉你忽略那个继承事实。 java.sql.Timestamp既有日期又有时间

java.sql.Timestamp 是你在这种情况下需要的类型,而不是 java.sql.Date。 (但请继续阅读以了解更好的类。)

早期版本的 Java 中的所有旧日期时间类都是勇敢的行业首次尝试解决日期时间处理问题,但它们不够好。 它们设计不良、令人困惑、麻烦。 避免使用它们:java.util.Datejava.util.Calendarjava.util.GregorianCalendarjava.text.DateFormatjava.text.SimpleDateFormat。 如果可能的话,也要避免使用 java.sql 类型。 而是使用 java.time 类。

JDBC 4.2

从 JDBC 4.2 开始,您的 JDBC 驱动程序可以直接访问来自数据库的 java.time 类型。 PreparedStatement::setObjectResultSet::getObject 方法可以将您的日期时间值从数据库呈现为 java.time 对象。

Instant instant = myResultSet.getObject( 1 );

...或许...

Instant instant = myResultSet.getObject( 1 , Instant.class );

如果您的驱动程序不够强大,则将简要转换为java.sql类型,然后立即自行转换为java.time。使用java.time执行所有业务逻辑。仅在与数据库交换数据时才使用java.sql类型。要进行java.time转换,请查找添加到旧类中的新方法。例如,java.sql.Timestamp::toInstant

在此处,我们从ResultSet中提取java.sql.Timestamp并立即将其转换为Instant。出于调试目的,您可能希望将其拆分为两行而不是一行。

Instant instant = myResultSet.getTimestamp( 1 ).toInstant();

我知道java.sql.Date的限制以及使用java.sql.Timestamp的方法。但是我还不了解java.time类,它们看起来非常有趣。+1 解释。 - Rodjf

1
Java SE 8还有一些其他常见用例的类。

YearMonth

这里有一个MonthDay类,包含月份和日期。用于表示生日等信息。

MonthDay

YearMonth类涵盖信用卡起始日期和到期日期使用情况以及人们没有指定日期的情况。

JDBC 4.2

Java SE 8中的JDBC将支持这些新类型,但不会有公共JDBC API更改。现有的通用setObject和getObject方法足以满足要求。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接