为什么OJDBC 7不将CHAR数据类型映射到Java字符串?

时间:2016-05-19 19:58:40

标签: java oracle11g ojdbc

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

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

在深入挖掘时,我们发现在OJDBC的StructMetaData包中有一个类oracle.jdbc.driver,它将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'。这个案例1'未在上面显示的第一个代码段中映射到java.lang.String

在更深入地看,这个案例1'映射到同一CHAR类的getColumnTypeName(int arg0 )方法中的StructMetaData

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&#39 ; s索引:

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

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

1 个答案:

答案 0 :(得分:0)

好抓......!

它看起来像一个bug;也许唯一的反对意见是甲骨文会非常容易忽视它。

Pro bug:

(请注意第一行,与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试图从支持的类型中删除CHAR。实际上,CHAR类型与VARCHAR2相比没有任何好处,而且一些缺点使它成为无选择。在过去,我喜欢&#34; CHAR&#34;与其他开发人员沟通,你打算定义一个具有预定义且固定长度固定长度的项目,但它甚至不会强制执行此操作(只需填充字符串,如果你用太短的值填充它)。 如果您有兴趣,可以在专题文章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并不好,当然也没有理由甲骨文在没有明确通知的情况下破坏现有的应用程序;因此,这是一个错误的假设显然更为明智。

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