java - 通过 Java JDBC 使用 iBATIS 的 Oracle SQL DATE 转换问题

标签 java oracle date jdbc ibatis

我目前正在努力解决使用 Java 中的 iBATIS 的 Oracle SQL DATE 转换问题。

我正在使用 Oracle JDBC 瘦驱动程序 ojdbc14 版本 10.2.0.4.0。 iBATIS 版本 2.3.2。 Java 1.6.0_10-rc2-b32。

问题围绕着此 SQL 片段返回的 DATE 类型的列:

SELECT *
FROM   TABLE(pk_invoice_qry.get_contract_rate(?,?,?,?,?,?,?,?,?,?)) order by from_date

包过程调用返回一个被包装在 TABLE 中的引用游标,然后很容易读取结果集,就像对表的选择查询一样。

在 PL/SQL Developer 中,返回的列之一,FROM_DATE,属于 SQL DATE 类型,具有一天中的时间精度:
Tue Dec 16 23:59:00 PST 2008

但是当我通过 iBATIS 和 JDBC 访问它时,该值只保留到今天的精度:
Tue Dec 16 12:00:00 AM PST 2008

像这样显示时会更清楚:

本来应该:
1229500740000 milliseconds since epoch
Tuesday, December 16, 2008 11:59:00 PM PST

但是得到这个:
1229414400000 milliseconds since epoch
Tuesday, December 16, 2008 12:00:00 AM PST
(as instance of class java.sql.Date)

无论我尝试什么,我都无法公开通过 Java JDBC 和 iBATIS 返回的此 DATE 列的完整精度。

iBATIS 所映射的内容是这样的:
FROM_DATE : 2008-12-03 : class java.sql.Date

当前的 iBATIS 映射是这样的:
<result property="from_date" jdbcType="DATE" javaType="java.sql.Date"/>

我也试过:
<result property="from_date" jdbcType="DATETIME" javaType="java.sql.Date"/>

或者
<result property="from_date" jdbcType="TIMESTAMP" javaType="java.sql.Timestamp"/>

但是所有尝试的映射都会产生相同的截断日期值。就好像 JDBC 在 iBATIS 还没接触到它之前就已经造成了丢失数据精度的损害。

显然,我通过 JDBC 和 iBATIS 丢失了一些数据精度,当我留在 PL/SQL Developer 中运行与测试脚本相同的 SQL 片段时不会发生这种情况。完全 Not Acceptable ,非常令人沮丧,最终非常可怕。

最佳答案

完整信息(它比此处描述的更复杂,可能取决于所使用的 Oracle 驱动程序的特定版本)在 Richard Yee 的回答中 - [Nabble 的链接现已过期]

在从 nabble 过期之前快速抓取...

罗杰,
见:http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-faq-090281.html#08_01

具体来说:
简单数据类型
DATE 和 TIMESTAMP 是怎么回事?
本节介绍简单数据类型。 :-)

在 9.2 之前,Oracle JDBC 驱动程序将 DATE SQL 类型映射到 java.sql.Timestamp。这在一定程度上是有意义的,因为 Oracle DATE SQL 类型与 java.sql.Timestamp 一样包含日期和时间信息。到 java.sql.Date 的更明显的映射有些问题,因为 java.sql.Date 不包括时间信息。也是RDBMS不支持TIMESTAMP SQL类型的情况,所以把DATE映射到Timestamp是没有问题的。

在 9.2 中,RDBMS 中添加了 TIMESTAMP 支持。 DATE 和 TIMESTAMP 之间的区别在于 TIMESTAMP 包括纳秒而 DATE 不包括纳秒。因此,从 9.2 开始,DATE 映射到 Date,TIMESTAMP 映射到 Timestamp。不幸的是,如果您依赖 DATE 值来包含时间信息,就会出现问题。

有几种方法可以解决这个问题:

更改您的表以使用 TIMESTAMP 而不是 DATE。这可能很少可能,但它是最好的解决方案。

更改您的应用程序以使用defineColumnType 将列定义为TIMESTAMP 而不是DATE。这有问题,因为除非必须,否则您真的不想使用defineColumnType(请参阅什么是defineColumnType 以及何时应该使用它?)。

更改您的应用程序以使用 getTimestamp 而不是 getObject。如果可能,这是一个很好的解决方案,但是许多应用程序包含依赖于 getObject 的通用代码,因此它并不总是可行的。

设置 V8Compatible 连接属性。这告诉 JDBC 驱动程序使用旧映射而不是新映射。您可以将此标志设置为连接属性或系统属性。您可以通过将连接属性添加到传递给 DriverManager.getConnection 或 OracleDataSource.setConnectionProperties 的 java.util.Properties 对象来设置它。您可以通过在 java 命令行中包含 -D 选项来设置系统属性。

java -Doracle.jdbc.V8Compatible="true"MyApp
Oracle JDBC 11.1 修复了这个问题。从此版本开始,驱动程序默认将 SQL DATE 列映射到 java.sql.Timestamp。无需设置 V8Compatible 即可获得正确的映射。 V8Compatible 已被强烈弃用。你根本不应该使用它。如果您确实将其设置为 true,则不会有任何伤害,但您应该停止使用它。

尽管很少以这种方式使用,但 V8Compatible 的存在并不是为了修复 DATE to Date 问题,而是为了支持与 8i 数据库的兼容性。 8i(及更早版本)数据库不支持 TIMESTAMP 类型。设置 V8Compatible 不仅会导致 SQL DATE 从数据库读取时映射到 Timestamp,还会导致所有 Timestamp 在写入数据库时​​都转换为 SQL DATE。由于不支持 8i,因此 11.1 JDBC 驱动程序不支持此兼容模式。因此,不支持 V8Compatible。

如上所述,11.1 驱动程序在从数据库读取时默认将 SQL DATE 转换为 Timestamp。这始终是正确的做法,而 9i 中的更改是一个错误。 11.1 驱动程序已恢复到正确的行为。即使您没有在应用程序中设置 V8Compatible,在大多数情况下您也不应该看到任何行为差异。如果您使用 getObject 读取 DATE 列,您可能会注意到不同。结果将是时间戳而不是日期。由于 Timestamp 是 Date 的子类,因此这通常不是问题。您可能会注意到不同之处在于,您是否依赖从 DATE 到 Date 的转换来截断时间组件,或者您是否对值执行 toString 操作。否则,更改应该是透明的。

如果出于某种原因,您的应用程序对这种更改非常敏感,并且您必须具有 9i-10g 行为,那么您可以设置一个连接属性。将 mapDateToTimestamp 设置为 false,驱动程序将恢复到默认的 9i-10g 行为并将 DATE 映射到 Date。

如果可能,您应该将列类型更改为 TIMESTAMP 而不是 DATE。

-理查德

罗杰·沃斯写道:
我在stackoverflow上发布了以下问题/问题,所以如果有人知道一个解决方案,很高兴看到它在那里得到回答:

通过 Java JDBC 使用 iBATIS 的 Oracle SQL DATE 转换问题

这是问题描述:

我目前正在努力解决使用 Java 中的 iBATIS 的 Oracle sql DATE 转换问题。

我正在使用 Oracle JDBC 瘦驱动程序 ojdbc14 版本 10.2.0.4.0。 iBATIS 版本 2.3.2。 Java 1.6.0_10-rc2-b32。

问题围绕着此 SQL 片段返回的 DATE 类型的列:

选择 *
FROM TABLE(pk_invoice_qry.get_contract_rate(?,?,?,?,?,?,?,?,?,?)) 按 from_date 排序

包过程调用返回一个被包装在 TABLE 中的引用游标,然后很容易读取结果集,就像对表的选择查询一样。

在 PL/SQL Developer 中,返回的列之一,FROM_DATE,属于 SQL DATE 类型,具有一天中的时间精度:

Tue Dec 16 23:59:00 PST 2008

但是当我通过 iBATIS 和 JDBC 访问它时,该值只保留到今天的精度:
Tue Dec 16 12:00:00 AM PST 2008

像这样显示时会更清楚:

本来应该:
自纪元以来的 1229500740000 毫秒
太平洋标准时间 2008 年 12 月 16 日,星期二晚上 11:59:00

但是得到这个:
自纪元以来的 1229414400000 毫秒
太平洋标准时间 2008 年 12 月 16 日,星期二 12:00:00
(作为类 java.sql.Date 的实例)

无论我尝试什么,我都无法公开通过 Java JDBC 和 iBATIS 返回的此 DATE 列的完整精度。

iBATIS 所映射的内容是这样的:

FROM_DATE : 2008-12-03 : 类 java.sql.Date

当前的 iBATIS 映射是这样的:

我也试过:

或者

但是所有尝试的映射都会产生相同的截断日期值。就好像 JDBC 在 iBATIS 接触它之前就已经造成了失去数据精度的损害。

很明显,我通过 JDBC 和 iBATIS 丢失了一些数据精度,当我留在 PL/SQL Developer 中运行与测试脚本相同的 SQL 片段时不会发生这种情况。完全 Not Acceptable ,非常令人沮丧,最终非常可怕。

关于java - 通过 Java JDBC 使用 iBATIS 的 Oracle SQL DATE 转换问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/383783/

相关文章:

java - Hadoop 2.x 中的 Snappy 压缩错误

java - 邮件发送异常 : Failed messages: com. sun.mail.smtp.SMTPSendFailedException

oracle - 为什么在尝试获取序列的当前值时会收到错误 ORA-08002?

mysql - SQL 查询字符串中的可选变量

.net - 计算两个日期之间的月数

php - 在 MySQL 中从 DOB 获取年龄

java - 如何打印以下 JUNG 对象返回的最短路径的顶点

oracle - 顶点始终执行验证

windows - 如何在批处理文件中获取一年中的某一天

java - @string 和@+string 的区别?