我目前正在使用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 to 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
这样显示时更清晰:
原本应该是:1229500740000毫秒,自2008年12月16日星期二,太平洋标准时间晚上11:59:00太平洋标准时间
但改为: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已经完成了丢失数据精度的损害.
很明显,当我在PL/SQL Developer中运行与测试脚本相同的SQL片段时,我通过浏览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时,我现在就像这样描述它: