Почему OJDBC 7 не сопоставляет тип данных CHAR со строкой Java?

Одной из задач OJDBC является сопоставление типов данных Oracle с типами Java.

Однако мы заметили, что если мы указываем тип данных CHAR, он не сопоставляется с java.lang.String. Это происходит в следующих версиях: OJDBC7 v12.1.0.2 и OJDBC6 v12.1.0.1. Более старые версии действительно отображали тип данных CHAR на: java.lang.String.

Копнув глубже, мы обнаружили, что существует класс: StructMetaData в пакете oracle.jdbc.driver OJDBC, который реализует сопоставление типа данных 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 при вызове для индекса этого столбца:

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

Если я заменю более старую версию файла jar OJDBC (например: 11.2.0.3), то будет возвращен тот же код: java.lang.String. Это баг или его убрали специально? Кто-нибудь сталкивался с такой же проблемой раньше?


person DolphinJava    schedule 19.05.2016    source источник
comment
Вы проверили примечания к выпуску драйвера? Если вы считаете, что это неправильно, откройте SR в My Oracle Support. Я был очень доволен предложенными решениями.   -  person Michael-O    schedule 19.05.2016


Ответы (1)


Хороший улов...!

Это действительно похоже на ошибку; возможно, единственный аргумент против заключается в том, что это было бы невероятно огромным упущением со стороны Oracle.

Про ошибка:

  • проблема обратной совместимости. Обновления драйвера jdbc нарушат работу существующих приложений, явно использующих объявление CHAR.
  • Это примечание о JPublisher типе данных и сопоставлении типов Java-Java описывает отображение по состоянию на 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 пытается исключить CHAR из поддерживаемых типов. Действительно, тип CHAR не имеет никаких преимуществ перед VARCHAR2, а несколько недостатков делают его безальтернативным. В прошлом мне нравился тот факт, что «CHAR» сообщает другим разработчикам о вашем намерении определить элемент с предопределенной и неизменно фиксированной длиной, но он даже не обеспечивает этого (он просто дополняет строку, если вы заполняете ее слишком короткое значение). Если вам интересно, есть раздел в отличной книге Expert Oracle Database Architecture - Oracle Database 9i, 10 г и | Томас Кайт | Пресс, посвященный теме. Вы можете прочитать отрывок в Спросить Том "Char Vs Varchar", в месте, где автор цитирует свою книгу:

Тот факт, что CHAR/NCHAR на самом деле является ничем иным, как замаскированным VARCHAR2/NVARCHAR2, наводит меня на мысль, что на самом деле есть только два типа символьных строк, которые следует рассматривать, а именно VARCHAR2 и NVARCHAR2. Я никогда не находил применения для типа CHAR ни в одном приложении. Поскольку тип CHAR всегда пустой, дополняет результирующую строку до фиксированной ширины, мы быстро обнаруживаем, что он потребляет максимум памяти как в сегменте таблицы, так и в любых сегментах индекса. Это было бы достаточно плохо, но есть еще одна важная причина, по которой следует избегать типов CHAR/NCHAR: они создают путаницу в приложениях, которым необходимо получить эту информацию (многие не могут «найти» свои данные после их сохранения). Причина этого связана с правилами сравнения строк символов и строгостью, с которой они выполняются. ....

[затем в том же посте следует отличный пример; это очень хорошо стоит прочитать]

Тот факт, что CHAR никуда не годятся, конечно же, не оправдывает Oracle от взлома существующих приложений без явного уведомления; так что гипотеза о том, что это баг, явно более разумна.

Учитывая, что теоретически это не будет иметь недостатков, в качестве крайнего обходного пути вы можете изменить все задействованные таблицы, чтобы переопределить их типы CHAR как VARCHAR2 (если вы имеете на это право и это возможно).

person Antonio    schedule 19.12.2016