通过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类型列:

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

包过程调用返回一个包含在TABLE中的ref游标,然后很容易读取结果集,好像是对表的select查询。

在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映射是这样的:

  

我也尝试过:

  

要么

  

但是所有尝试的映射都会产生相同的截断Date值。 就好像在iBATIS甚至触及它之前,JDBC已经完成了丢失数据精度的损害。

很显然,当我在PL / SQL Developer中运行与测试脚本相同的SQL片段时,我通过浏览JDBC和iBATIS而失去了一些数据精度。 根本不可接受,非常令人沮丧,最终非常可怕。

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


从nabble到期之前快速抓取……

Roger,见: 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 TIMESTAMP支持中添加了RDBMS。 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到Date的问题,而是支持与8i数据库的兼容性。 8i(和更早版本)数据库不支持TIMESTAMP类型。 设置V8Compatible不仅会导致SQL DATE在从数据库读取时映射到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。

-Richard


Roger Voss写道:我在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类型列:

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

包过程调用返回一个包含在TABLE中的ref游标,然后很容易读取结果集,好像是对表的select查询。

在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 

这样显示时更清晰:

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

但改为:1229414400000毫秒,自2008年12月16日星期二,星期二,太平洋标准时间上午12点00分(作为类java.sql.Date的实例)

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

iBATIS映射的是:

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

目前的iBATIS映射是这样的:

我也尝试过:

要么

但是所有尝试的映射都会产生相同的截断Date值。 就好像在iBATIS甚至触及它之前,JDBC已经完成了丢失数据精度的损害。

显然,当我停留在运行与测试脚本相同的SQL片段的PL / SQL Developer时,我通过浏览JDBC和iBATIS而失去了一些数据精度。 根本不可接受,非常令人沮丧,最终非常可怕。

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

   

然后添加了这个实现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时,我现在将其描述为:

  

我用jdbcType =“TIMESTAMP”而不是jdbcType =“DATE”解决了我的问题

•问题:

•解决了:

问候。

佩德罗

问题出在Oracle驱动程序上。

我找到的最佳解决方案是将所有jdbcType =“DATE”更改为jdbcType =“TIMESTAMP”,将所有#column_name:DATE#更改为#column_name:TIMESTAMP#

所以改变:

  

  

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

是的,我明白了 – 纯SQL DATE标准必须只存储到日期分辨率。 实际上,这是Oracle DATE类型的片段:

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

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

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

不幸的是,当我将映射更改为:

  

要么

  

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

Richard Yee提到Oracle的最新驱动程序解决了这个问题。 我可以证实这一点。 与10.2驱动程序有同样的问题,今天升级到ojdbc5.jar(11.2.0.1.0),问题现在消失了。