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

发布于 2024-07-10 06:23:59 字数 1646 浏览 8 评论 0原文

我目前正在使用 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 中,返回的 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 片段时,这种情况就不会发生。 根本不可接受,非常令人沮丧,最终非常可怕。

I'm currently wrestling with an Oracle SQL DATE conversion problem using iBATIS from Java.

Am using the Oracle JDBC thin driver ojdbc14 version 10.2.0.4.0. iBATIS version 2.3.2. Java 1.6.0_10-rc2-b32.

The problem revolves around a column of DATE type that is being returned by this snippet of SQL:

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

The package procedure call returns a ref cursor that is being wrapped in a TABLE to where is then easy to read the result set as though were a select query against a table.

In PL/SQL Developer, one of the columns returned, FROM_DATE, of SQL DATE type, has precision to time of day:

Tue Dec 16 23:59:00 PST 2008

But when I access this via iBATIS and JDBC, the value only retains precision to day:

Tue Dec 16 12:00:00 AM PST 2008

This is clearer when displayed like so:

Should have been:

1229500740000 milliseconds since epoch
Tuesday, December 16, 2008 11:59:00 PM PST

But getting this instead:

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

No matter what I try, I am unable to expose the full precision of this DATE column to be returned via Java JDBC and iBATIS.

What iBATIS is mapping from is this:

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

The current iBATIS mapping is this:

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

I've also tried:

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

or

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

But all attempted mappings yield the same truncated Date value. It's as though JDBC has already done the damage of losing data precision before iBATIS even touches it.

Clearly I'm losing some of my data precision by going through JDBC and iBATIS that is not happening when I stay in PL/SQL Developer running the same SQL snippet as a test script. Not acceptable at all, very frustrating, and ultimately very scary.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

携余温的黄昏 2024-07-17 06:23:59

完整的信息(比这里描述的更复杂,可能取决于正在使用的 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 映射到时间戳,还会导致写入数据库时​​所有时间戳转换为 SQL DATE。 由于 8i 已不再支持,因此 11.1 JDBC 驱动程序不支持此兼容模式。 因此,不再支持 V8Compatible。

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

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

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

——理查德


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

Oracle SQL DATE conversion Problem using iBATIS via Java JDBC

这是问题描述:

我目前正在与 Oracle sql DATE 摔跤使用 Java 中的 iBATIS 进行转换问题。

我使用 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 中,返回的 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
(作为类 java.sql.Date 的实例)

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

iBATIS 的映射来源是这样的:

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

当前的 iBATIS 映射是这样的:

我也尝试过:

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

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

The full info (and it's more complex than described here and might depend upon which particular version of the Oracle drivers are in use) is in Richard Yee's answer here - [now expired link to Nabble]


Quick grab before it expires from nabble...

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

Specifically:
Simple Data Types
What is going on with DATE and TIMESTAMP?
This section is on simple data types. :-)

Prior to 9.2, the Oracle JDBC drivers mapped the DATE SQL type to java.sql.Timestamp. This made a certain amount of sense because the Oracle DATE SQL type contains both date and time information as does java.sql.Timestamp. The more obvious mapping to java.sql.Date was somewhat problematic as java.sql.Date does not include time information. It was also the case that the RDBMS did not support the TIMESTAMP SQL type, so there was no problem with mapping DATE to Timestamp.

In 9.2 TIMESTAMP support was added to the RDBMS. The difference between DATE and TIMESTAMP is that TIMESTAMP includes nanoseconds and DATE does not. So, beginning in 9.2, DATE is mapped to Date and TIMESTAMP is mapped to Timestamp. Unfortunately if you were relying on DATE values to contain time information, there is a problem.

There are several ways to address this problem:

Alter your tables to use TIMESTAMP instead of DATE. This is probably rarely possible, but it is the best solution when it is.

Alter your application to use defineColumnType to define the columns as TIMESTAMP rather than DATE. There are problems with this because you really don't want to use defineColumnType unless you have to (see What is defineColumnType and when should I use it?).

Alter you application to use getTimestamp rather than getObject. This is a good solution when possible, however many applications contain generic code that relies on getObject, so it isn't always possible.

Set the V8Compatible connection property. This tells the JDBC drivers to use the old mapping rather than the new one. You can set this flag either as a connection property or a system property. You set the connection property by adding it to the java.util.Properties object passed to DriverManager.getConnection or to OracleDataSource.setConnectionProperties. You set the system property by including a -D option in your java command line.

java -Doracle.jdbc.V8Compatible="true" MyApp
Oracle JDBC 11.1 fixes this problem. Beginning with this release the driver maps SQL DATE columns to java.sql.Timestamp by default. There is no need to set V8Compatible to get the correct mapping. V8Compatible is strongly deprecated. You should not use it at all. If you do set it to true it won't hurt anything, but you should stop using it.

Although it was rarely used that way, V8Compatible existed not to fix the DATE to Date issue but to support compatibility with 8i databases. 8i (and older) databases did not support the TIMESTAMP type. Setting V8Compatible not only caused SQL DATE to be mapped to Timestamp when read from the database, it also caused all Timestamps to be converted to SQL DATE when written to the database. Since 8i is desupported, the 11.1 JDBC drivers do not support this compatibility mode. For this reason V8Compatible is desupported.

As mentioned above, the 11.1 drivers by default convert SQL DATE to Timestamp when reading from the database. This always was the right thing to do and the change in 9i was a mistake. The 11.1 drivers have reverted to the correct behavior. Even if you didn't set V8Compatible in your application you shouldn't see any difference in behavior in most cases. You may notice a difference if you use getObject to read a DATE column. The result will be a Timestamp rather than a Date. Since Timestamp is a subclass of Date this generally isn't a problem. Where you might notice a difference is if you relied on the conversion from DATE to Date to truncate the time component or if you do toString on the value. Otherwise the change should be transparent.

If for some reason your app is very sensitive to this change and you simply must have the 9i-10g behavior, there is a connection property you can set. Set mapDateToTimestamp to false and the driver will revert to the default 9i-10g behavior and map DATE to Date.

If possible, you should change your column type to TIMESTAMP instead of DATE.

-Richard


Roger Voss wrote:
I posted following question/problem on stackoverflow, so if anyone knows a resolution, would be good to see it answered there:

Oracle SQL DATE conversion problem using iBATIS via Java JDBC

Here's the problem description:

I'm currently wrestling with an Oracle sql DATE conversion problem using iBATIS from Java.

Am using the Oracle JDBC thin driver ojdbc14 version 10.2.0.4.0. iBATIS version 2.3.2. Java 1.6.0_10-rc2-b32.

The problem revolves around a column of DATE type that is being returned by this snippet of SQL:

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

The package procedure call returns a ref cursor that is being wrapped in a TABLE to where is then easy to read the result set as though were a select query against a table.

In PL/SQL Developer, one of the columns returned, FROM_DATE, of SQL DATE type, has precision to time of day:

Tue Dec 16 23:59:00 PST 2008

But when I access this via iBATIS and JDBC, the value only retains precision to day:

Tue Dec 16 12:00:00 AM PST 2008

This is clearer when displayed like so:

Should have been:
1229500740000 milliseconds since epoch
Tuesday, December 16, 2008 11:59:00 PM PST

But getting this instead:
1229414400000 milliseconds since epoch
Tuesday, December 16, 2008 12:00:00 AM PST
(as instance of class java.sql.Date)

No matter what I try, I am unable to expose the full precision of this DATE column to be returned via Java JDBC and iBATIS.

What iBATIS is mapping from is this:

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

The current iBATIS mapping is this:

I've also tried:

or

But all attempted mappings yield the same truncated Date value. It's as though JDBC has already done the damage of loosing data precision before iBATIS even touches it.

Clearly I'm loosing some of my data precision by going through JDBC and iBATIS that is not happening when I stay in PL/SQL Developer running the same SQL snippet as a test script. Not acceptable at all, very frustrating, and ultimately very scary.

笑梦风尘 2024-07-17 06:23:59

我找到了如何解决这个问题。 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"/>

I found out how to solve this problem. iBATIS permits custom type handlers to be registered. So in my sqlmap-config.xml file I added this:

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

And then added this class which implements the iBATIS TypeHandlerCallback interface:

// 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);
    }
}

Whennever I need to map an Oracle DATE I now describe it like so:

<result property="from_date" jdbcType="DATETIME" javaType="date"/>
手长情犹 2024-07-17 06:23:59

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

• 问题:

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

• 已解决:

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

I have solved my problem using jdbcType="TIMESTAMP" instead of jdbcType="DATE"

• PROBLEM:

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

• SOLVED:

<result column="MY_UTC_POS" property="myUtcPosition" jdbcType="TIMESTAMP" />
颜漓半夏 2024-07-17 06:23:59

问题出在 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"/>

The problem is with the Oracle Driver.

The best solution I found was to change all jdbcType="DATE" to jdbcType="TIMESTAMP"
and all #column_name:DATE# to #column_name:TIMESTAMP#

So change:

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

to

<result property="from_date" jdbcType="TIMESTAMP" javaType="java.sql.Date"/>
在风中等你 2024-07-17 06:23:59

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

The problem is the use of java.sql.Date. According to the Javadoc, the millisecond values wrapped by a java.sql.Date instance must be 'normalized' by setting the hours, minutes, seconds, and milliseconds to zero in the particular time zone with which the instance is associated, to conform with the definition of SQL DATE.

梦断已成空 2024-07-17 06:23:59

是的,我明白了 - 简单的 SQL DATE 标准必须只存储到日期分辨率。 事实上,这里是 Oracle DATE 类型的一个片段:

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 并丢失了一天中的时间精度。

Yes, I see - the plain SQL DATE standard must be to only store to day resolution. Indeed, here is a snippet on Oracle's DATE type:

Oracle supports both date and time,
albeit differently from the SQL2
standard. Rather than using two
separate entities, date and time,
Oracle only uses one, DATE. The DATE
type is stored in a special internal
format that includes not just the
month, day, and year, but also the
hour, minute, and second.

Which makes the point that Oracle's DATE exceeds standard SQL DATE.

Hmm, Oracle PL/SQL folks use DATE extensively to hold values where they depend on the resolution being to the second. Looks like iBATIS needs something like the Hibernate sql dialect concept where instead of interpreting DATE via java.sql.Date, could override and instead interpret via java.util.Date, which Javadocs defines as permitting millisecond resolution.

Unfortunately when I've changed the mapping to something like:

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

or

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

It's still seemingly first translated the SQL DATE to a java.sql.Date and lost the time of day precision.

溇涏 2024-07-17 06:23:59

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

Richard Yee mentions that Oracle's latest drivers fix the problem. I can confirm that. Had the same problem here with 10.2 drivers, upgraded today to ojdbc5.jar (11.2.0.1.0), and the problem is gone now.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文