Java 结果集#getDate()语义

Java 结果集#getDate()语义,java,oracle,datetime,jdbc,Java,Oracle,Datetime,Jdbc,我们从迁移到ojdbc6-11.2.0.3.0到ojdbc7-12.1.0.1,并观察到ResultSet#getDate()语义的变化。以前,返回的java.sql.Date将根据java.sql.Date上指定的契约,通过将小时、分钟、秒和毫秒设置为零来“规范化”。使用ojdbc7时,情况不再如此,java.sql.Date根据数据库上的值设置小时、分钟、秒和毫秒 我查看了ResultSet#getDate()的Javadoc,它没有明确指出哪种行为是正确的。我会假设旧的行为是规范的意图。我

我们从迁移到
ojdbc6-11.2.0.3.0
ojdbc7-12.1.0.1
,并观察到
ResultSet#getDate()语义的变化。以前,返回的
java.sql.Date
将根据
java.sql.Date
上指定的契约,通过将小时、分钟、秒和毫秒设置为零来“规范化”。使用
ojdbc7
时,情况不再如此,
java.sql.Date
根据数据库上的值设置小时、分钟、秒和毫秒


我查看了
ResultSet#getDate()
的Javadoc,它没有明确指出哪种行为是正确的。我会假设旧的行为是规范的意图。我说得对吗?我们遇到驱动程序错误了吗?

正如类
java.sql.Date
的文档所述,这显然是一个错误

为了符合SQL日期的定义,必须通过将实例关联的特定时区中的小时、分钟、秒和毫秒设置为零来“规范化”java.SQL.DATE实例包装的毫秒值


Oracle没有更改JDBC规范。我们已经更新了Oracle数据库JDBC驱动程序文档。如果文件中有任何内容令人困惑或不正确,我们将予以纠正。不过,JDBC规范没有任何更改

以前版本的驱动程序不一致。在某些地方,他们将秒数归零。在其他地方,他们没有。在12.1中,我们使驱动程序一致。问题是,什么是正确的行为。无论哪种方式,一些客户都会看到行为的改变

经过长时间激烈的辩论,我们决定,对于Oracle数据库客户来说,最好的办法不是将秒数归零。让我解释一下

  • ANSI SQL日期类型不存储秒数。JDBC规范主要采用ANSI SQL
  • Oracle数据库日期类型不存储秒数。ANSI SQL委员会的成员向我保证,这完全符合ANSI SQL
  • JDBC的目标是公开数据库。JDBC规范没有定义一些抽象数据库,目的是驱动程序实现该抽象数据库。JDBC驱动程序需要公开数据库的详细信息,而不是隐藏它们。JDBC定义了一些工具来抽象一些数据库细节,但是程序员可以选择使用或不使用这些工具
  • java.sql.Date
    不强制执行零秒行为,尽管它很容易执行。程序有责任强制执行或不强制执行该行为
所以,甲骨文日期有秒。Oracle JDBC驱动程序公开Oracle数据库。如果
getDate
将秒数归零,则会丢失数据。对于一些用户来说,这并不重要,但对于其他用户来说,这很重要。由于Oracle日期存储秒数,许多Oracle数据库在日期列中以秒精度存储时间。在这些情况下,将秒数归零将丢失信息

如果程序将非零秒的日期传递到
setDate
,则程序创建了不符合要求的日期。如果驱动程序将秒数归零,则驱动程序已丢弃程序提供的信息以及数据库可以存储的信息。驾驶员再次丢失了信息

编写SQL或Java使get和set上的秒数都为零非常简单。解决信息丢失的问题更加困难,尽管这肯定是可能的

因此,我们选择让驱动程序一致地将秒数保持在
java.sql.Date
s上
ResultSet.getDate
可以构造一个非零秒的
java.sql.Date
,但是这准确地反映了数据库中的内容。如前所述,实施日期本可以强制执行这一点,但没有。一种方法是程序在数据库中存储数据时创建日期,因此这是程序的责任。驱动程序只是使用程序提供的数据


我真的很抱歉,以前司机的态度不一致。我们一直在努力清理不一致和奇怪的角落案例。但我们拥有庞大的安装基础。每次我们改变一些东西,甚至是明显的错误修复,都会影响到某个地方的一些客户。因此,我们试图在改进驱动程序和保持向后兼容性之间取得平衡。12c是一个主要版本。我们借此机会做出了一些更明显的改变。我们对中断表示遗憾,但认为这对客户整体来说是正确的。

我在11g数据库上分析了这个问题,可以确认shonky linux用户提到的
PreparedStatement.setDate(index,java.sql.Date)
(我测试了
ojdbc6-11.2.0.4-g
ojdbc7-12.1.0.2
)的行为

虽然新的行为可能没有明确违反JDBC规范,但它肯定不适合使用
java.sql.Date
java.sql.Time
java.sql.Timestamp
以及相应的数据类型
types.Date
,这一概念,
类型.时间
类型.时间戳


因此,尽管Douglas Surber所解释的“减少现有Oracle客户的痛苦”的决定可能有意义,但从(独立于DBMS的)JDBC的角度来看,它似乎并不正确。

ojdbc7-12.1.0.1中出现的另一个相关问题:PreparedStatement.setDate(index,java.sql.Date)在将值存储到数据库之前,不会截断任何时间组件。它可能只出现在10g上(我没有11g或12c在其他版本上进行测试)。感谢您的解释。jdbc规范说java.sql.Date没有时间组件(与java.sql.Timestamp相反)。时间组件在许多业务上下文(例如出生日期)中毫无意义,这种更改将导致在业务上下文中使用此行为的现有应用程序在迁移到12c时中断。如果开发