【问题标题】:Why doesn't OJDBC 7 map the CHAR data type to a Java String?为什么 OJDBC 7 不将 CHAR 数据类型映射到 Java 字符串?
【发布时间】:2016-09-16 21:43:54
【问题描述】:

OJDBC 的工作之一是将 Oracle 数据类型映射到 Java 类型。

但是,我们注意到,如果我们提供 CHAR 数据类型,它不会映射到 java.lang.String。显示此行为的版本是:OJDBC7 v12.1.0.2OJDBC6 v12.1.0.1。旧版本确实将CHAR 数据类型映射到:java.lang.String

在深入挖掘时,我们发现在 OJDBC 的 oracle.jdbc.driver 包中存在一个类:StructMetaData,它实现了 Oracle 数据类型到 Java 类型的映射。其中有一个方法:'getColumnClassName(int arg0)' 值得关注。我们注意到,对于 OJDBC v7,映射到java.lang.String 的案例如下:

    int arg1 = this.getColumnType(arg0);
    switch (arg1) {
    case -104:
        return "oracle.sql.INTERVALDS";
    case -103:
        return "oracle.sql.INTERVALYM";
    case -102:
        return "oracle.sql.TIMESTAMPLTZ";
    case -101:
        return "oracle.sql.TIMESTAMPTZ";
    case -15:
    case -9:
    case 12:
        return "java.lang.String";
     ...

但是,在旧的 OJDBC 实现中,它看起来像这样:

    int arg1 = this.getColumnType(arg0);
    switch (arg1) {
    case -104:
        return "oracle.sql.INTERVALDS";
    case -103:
        return "oracle.sql.INTERVALYM";
    case -102:
        return "oracle.sql.TIMESTAMPLTZ";
    case -101:
        return "oracle.sql.TIMESTAMPTZ";
    case -15:
    case -9:
    case 1:
    case 12:
        return "java.lang.String";
    ...

在后一种情况下,还有一个映射到java.lang.String 的情况,即。 '情况1'。在上面显示的第一个代码 sn-p 中,此“案例 1”未映射到 java.lang.String

深入研究,这个“案例 1”被映射到同一 StructMetaData 类的 getColumnTypeName(int arg0 ) 方法中的 CHAR

public String getColumnTypeName(int arg0) throws SQLException {
    int arg1 = this.getColumnType(arg0);
    int arg2 = this.getValidColumnIndex(arg0);
    switch (arg1) {
    case -104:
        return "INTERVALDS";
    case -103:
        return "INTERVALYM";
    case -102:
        return "TIMESTAMP WITH LOCAL TIME ZONE";
    case -101:
        return "TIMESTAMP WITH TIME ZONE";
    case -15:
        return "NCHAR";
    case -13:
        return "BFILE";
    case -9:
        return "NVARCHAR";
    case -2:
        return "RAW";
    case 1:
        return "CHAR";
 ...

因此,如果我们使用 OJDBC 7 或 OJDBC6 v12.1.0.1 并指定 CHAR 作为列的数据类型,则以下代码在调用该列的索引时返回 null

 for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
     ...
     resultSetMetaData.getColumnClassName(columnIndex)
     ...

如果我替换旧版本的 OJDBC jar(例如:11.2.0.3),则返回相同的代码:java.lang.String。这是一个错误还是被设计删除? 以前有人遇到过同样的问题吗?

【问题讨论】:

  • 您查看过驱动程序的发行说明吗?如果您认为这是错误的,请通过 My Oracle Support 打开 SR。我对所提供的解决方案非常满意。

标签: java oracle11g ojdbc


【解决方案1】:

好收获...!

它看起来真的像一个错误;也许唯一反对的论点是甲骨文对它的巨大忽视。

专业错误:

  • 向后兼容性问题。 jdbc 驱动程序的更新会破坏显式依赖CHAR 声明的现有应用程序
  • 这篇关于 JPublisher Data Type and Java-to-Java Type Mappings 的说明描述了从 12c 开始的映射

(注意第一行,与CHAR 类型相关,映射到java.lang.String

| SQL and PL/SQL Data Type | Oracle Mapping   | JDBC Mapping
|------------------------- |------------------|-----------------------------------------------
| CHAR, CHARACTER, LONG,   |                  | 
|STRING, VARCHAR, VARCHAR2 | oracle.sql.CHAR  | java.lang.String
| NCHAR, NVARCHAR2         | oracle.sql.NCHAR | oracle.sql.NString 
| NCLOB                    | oracle.sql.NCLOB | oracle.sql.NCLOB 

针对错误:

  • 一个奇特的假设可能认为 Oracle 正试图从支持的类型中排除 CHARs。事实上,CHAR 类型确实比VARCHAR2 没有任何好处,并且有一些缺点使它无法选择。在过去,我喜欢这样一个事实,即“CHAR”向其他开发人员传达您定义具有预定义且始终固定长度的项目的意图,但它甚至不强制执行这一点(它只是填充字符串,如果您填写它的值太短)。 如果你有兴趣,在优秀的书Expert Oracle Database Architecture - Oracle Database 9i, 10g, and | Thomas Kyte | Apress 中有一个专门讨论这个主题的部分。您可以在Ask Tom "Char Vs Varchar" 中阅读摘录,作者引用了他自己的书:

CHAR/NCHAR 实际上只不过是变相的 VARCHAR2/NVARCHAR2,这让我认为实际上只有两种字符串类型需要考虑,即 VARCHAR2 和 NVARCHAR2。我从未在任何应用程序中找到 CHAR 类型的用途。由于 CHAR 类型总是将生成的字符串填充为固定宽度,因此我们很快发现它在表段和任何索引段中都消耗了最大的存储空间。这已经够糟糕了,但还有另一个重要的原因要避免使用 CHAR/NCHAR 类型:它们在需要检索此信息的应用程序中造成混乱(许多在存储数据后无法“找到”它们的数据)。其原因与字符串比较的规则及其执行的严格性有关。 ....

[那么在同一篇文章中有一个很好的例子;非常值得一读]

CHAR 不好的事实当然不能证明 Oracle 在没有明确通知的情况下破坏现有应用程序是正当的;所以假设它是一个错误显然更明智。

鉴于理论上它不会有缺点,作为一种极端的解决方法,您可以更改所有涉及的表以将其 CHAR 类型重新定义为 VARCHAR2(如果您有权这样做,并且可行)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-26
    • 1970-01-01
    • 1970-01-01
    • 2014-11-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多