【问题标题】:Oracle SQL DATE conversion problem using iBATIS via Java JDBC通过 Java JDBC 使用 iBATIS 的 Oracle SQL DATE 转换问题
【发布时间】:2010-09-27 21:08:21
【问题描述】:

我目前正在努力解决使用来自 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 的 sn-p 返回的 DATE 类型的列:

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

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

在 PL/SQL Developer 中,返回的列之一是 SQL DATE 类型的 FROM_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 sn-p 作为测试脚本时,这不会发生。完全不能接受,非常令人沮丧,最终非常可怕。

【问题讨论】:

    标签: java oracle date jdbc ibatis


    【解决方案1】:

    完整信息(它比此处描述的更复杂,并且可能取决于正在使用的特定版本的 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 转换为时间戳。这始终是正确的做法,而 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 的 sn-p 返回的 DATE 类型的列:

    选择 * FROM TABLE(pk_invoice_qry.get_contract_rate(?,?,?,?,?,?,?,?,?,?)) 由 from_date 订购

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

    在 PL/SQL Developer 中,返回的列之一是 SQL DATE 类型的 FROM_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 AM (作为类 java.sql.Date 的实例)

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

    iBATIS 的映射是这样的:

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

    当前的 iBATIS 映射是这样的:

    我也试过了:

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

    很明显,当我在 PL/SQL Developer 中运行与测试脚本相同的 SQL sn-p 时,我通过 JDBC 和 iBATIS 会丢失一些数据精度。完全不能接受,非常令人沮丧,最终非常可怕。

    【讨论】:

    • @Gwyn Evans 您在回答中给出的链接已损坏。用工作链接更新它们。
    • Nabble one's 不见了(但文本在答案中),但我找到了它所指的常见问题解答的新位置......
    【解决方案2】:

    我发现了如何解决这个问题。 iBATIS 允许注册自定义类型处理程序。所以在我的 sqlmap-config.xml 文件中我添加了这个:

    <typeAlias alias="OracleDateHandler" type="com.tideworks.ms.CustomDateHandler"/>
    <typeHandler callback="OracleDateHandler" jdbcType="DATETIME" javaType="date"/>
    

    然后添加这个实现iBATIS TypeHandlerCallback接口的类:

    // corrected getResult()/setParameter() to correctly deal with when value is null
    public class CustomDateHandler implements TypeHandlerCallback {
        @Override
        public Object getResult(ResultGetter getter) throws SQLException {
            final Object obj = getter.getTimestamp();
            return obj != null ? (Date) obj : null;
        }
    
        @Override
        public void setParameter(ParameterSetter setter,Object value) throws SQLException {
            setter.setTimestamp(value != null ? new Timestamp(((Date)value).getTime()) : null);
        }
    
        @Override
        public Object valueOf(String datetime) {
            return Timestamp.valueOf(datetime);
        }
    }
    

    当我不需要映射 Oracle DATE 时,我现在这样描述它:

    <result property="from_date" jdbcType="DATETIME" javaType="date"/>
    

    【讨论】:

    • 不错,我不确定如何解决这个问题,但很高兴我能指出正确的方向
    • 我看不到你的导入。当您将值转换为日期时,是 util 还是 sql?
    【解决方案3】:

    我已经使用jdbcType="TIMESTAMP" 而不是jdbcType="DATE" 解决了我的问题

    • 问题:

    <result column="MY_UTC_POS" property="myUtcPosition" jdbcType="DATE" />
    

    • 已解决:

    <result column="MY_UTC_POS" property="myUtcPosition" jdbcType="TIMESTAMP" />
    

    【讨论】:

    • 原来使用 TIMESTAMP 的结果是否成功取决于您正在处理的 Oracle JDBC 驱动程序的版本。他们一直在改变他们的策略,如何将 PL/SQL DATE 类型的完整毫秒精度转换为 java.util.Date 类型。
    • 它适用于 MyBatis 3.0.5 和 ojdbc14 10.2.0.4.0(可能是因为新的类型处理程序)
    【解决方案4】:

    问题出在 Oracle 驱动程序上。

    我发现的最佳解决方案是将所有 jdbcType="DATE" 更改为 jdbcType="TIMESTAMP" 以及所有 #column_name:DATE# 到 #column_name:TIMESTAMP#

    所以改变:

    <result property="from_date" jdbcType="DATE" javaType="java.sql.Date"/>
    

    <result property="from_date" jdbcType="TIMESTAMP" javaType="java.sql.Date"/>
    

    【讨论】:

      【解决方案5】:

      问题在于java.sql.Date的使用。根据Javadoc,由java.sql.Date 实例包装的毫秒值必须通过在实例关联的特定时区中将小时、分钟、秒和毫秒设置为零来“标准化”,以符合SQLDATE的定义。

      【讨论】:

        【解决方案6】:

        是的,我明白了——普通的 SQL DATE 标准必须是只存储到一天的分辨率。事实上,这里有一个关于 Oracle 的 DATE 类型的 sn-p:

        Oracle 同时支持日期和时间, 尽管与 SQL2 不同 标准。而不是使用两个 单独的实体、日期和时间, Oracle 只使用一个,DATE。日期 类型存储在一个特殊的内部 格式不仅包括 月、日、年,还有 小时、分钟和秒。

        这表明 Oracle 的 DATE 超过了标准 SQL DATE。

        嗯,Oracle PL/SQL 人员广泛使用 DATE 来保存他们依赖于秒的分辨率的值。看起来 iBATIS 需要类似于 Hibernate sql 方言概念的东西,而不是通过 java.sql.Date 解释 DATE,而是可以覆盖并改为通过 java.util.Date 解释,Javadocs 将其定义为允许毫秒分辨率。

        不幸的是,当我将映射更改为以下内容时:

        <result property="from_date" jdbcType="DATE" javaType="java.util.Date"/>
        

        <result property="from_date" jdbcType="DATETIME" javaType="java.util.Date"/>
        

        它似乎仍然首先将 SQL DATE 转换为 java.sql.Date 并且丢失了时间精度。

        【讨论】:

          【解决方案7】:

          Richard Yee 提到 Oracle 的最新驱动程序解决了这个问题。我可以确认。 10.2的驱动也有同样的问题,今天升级到ojdbc5.jar(11.2.0.1.0),问题就解决了。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-03-19
            • 2012-05-05
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多