Кодировка Oracle JDBC и ограничение в 4000 символов

Мы пытаемся сохранить строку в кодировке UTF-16 в базе данных Oracle AL32UTF8.

Наша программа отлично работает с базой данных, которая использует WE8MSWIN1252 в качестве кодировки. Когда мы пытаемся запустить его в базе данных, которая использует AL32UTF8, он попадает в java.sql.SQLException: ORA-01461: can bind a LONG value only for insert into a LONG column.

В приведенном ниже тестовом примере все работает нормально, если наши входные данные не становятся слишком длинными.

Входная строка может превышать 4000 символов. Мы хотим сохранить как можно больше информации, даже если понимаем, что ввод необходимо отключить.

Наши таблицы базы данных определены с помощью ключевого слова CHAR (см. Ниже). Мы надеялись, что это позволит нам хранить до 4000 символов любого набора символов. Можно ли это сделать? Если да, то как?

Мы безуспешно пытались преобразовать String в UTF8 с помощью ByteBuffer. OraclePreparedStatement.setFormOfUse(...) тоже нам не помог.

Переход на CLOB не вариант. Если веревка слишком длинная, ее нужно разрезать.

Это наш код на данный момент:

public static void main(String[] args) throws Exception {
    String ip ="193.53.40.229";
    int port = 1521;
    String sid = "ora11";
    String username = "obasi";
    String password = "********";

    String driver = "oracle.jdbc.driver.OracleDriver";
    String url = "jdbc:oracle:thin:@" + ip + ":" + port + ":" + sid;
    Class.forName(driver);

    String shortData = "";
    String longData = "";
    String data;

    for (int i = 0; i < 5; i++)
        shortData += "é";

    for (int i = 0; i < 4000; i++)
        longData += "é";

    Connection conn = DriverManager.getConnection(url, username, password);

    PreparedStatement stat = null;
    try  {
        stat = conn.prepareStatement("insert into test_table_short values (?)");
        data = shortData.substring(0, Math.min(5, shortData.length()));
        stat.setString(1, data);
        stat.execute();

        stat = conn.prepareStatement("insert into test_table_long values (?)");
        data = longData.substring(0, Math.min(4000, longData.length()));
        stat.setString(1, data);
        stat.execute();
    } finally {
        try {
            stat.close();
        } catch (Exception ex){}
    }
}

Это сценарий создания простой таблицы:

CREATE TABLE test_table_short (
    DATA    VARCHAR2(5 CHAR);
);

CREATE TABLE test_table_long (
    DATA    VARCHAR2(4000 CHAR);
);

Тестовый пример отлично работает на коротких данных. Однако на длинных данных он продолжает получать ошибку. Даже когда наш longData состоит всего из 3000 символов, он все равно не выполняется успешно.

Заранее спасибо!


person Arolition    schedule 19.07.2012    source источник


Ответы (2)


До Oracle 12.1 столбец VARCHAR2 ограничивался хранением 4000 байтов данных в наборе символов базы данных, даже если он объявлен VARCHAR2(4000 CHAR). Поскольку для каждого символа в вашей строке требуется 2 байта памяти в наборе символов UTF-8, вы не сможете хранить более 2000 символов в столбце. Конечно, это число изменится, если некоторые из ваших персонажей действительно требуют всего 1 байт памяти или если некоторым из них требуется более 2 байтов памяти. Когда набором символов базы данных является Windows-1252, каждый символ в вашей строке требует только одного байта памяти, поэтому вы сможете хранить 4000 символов в столбце.

Поскольку у вас более длинные строки, можно ли объявить столбец как CLOB, а не как VARCHAR2? Это (эффективно) сняло бы ограничение длины (существует ограничение на размер CLOB, которое зависит от версии Oracle и размера блока, но, по крайней мере, в диапазоне нескольких ГБ).

Если вы используете Oracle 12.1 или новее, параметр max_string_size позволяет увеличьте максимальный размер столбца VARCHAR2 с 4000 до 32767 байт.

person Justin Cave    schedule 19.07.2012
comment
Спасибо за ваш ответ. К сожалению, в этом случае для нас не может быть и речи об использовании clob. Согласно ссылке, это правильный ответ. Однако, по моему скромному мнению, ссылка вводит в заблуждение. Знаете ли вы, где это объясняется в документации? Мы много искали, но не нашли этого. - person Arolition; 19.07.2012
comment
@Arolition - Я добавил комментарий в ветку SO. Ответ правильный, поскольку он идет. Он просто не отмечает, что если для определенных 4000 символов требуется более 4000 байтов памяти, ограничение емкости в 4000 байтов все еще срабатывает. - person Justin Cave; 19.07.2012
comment
UTF-8 - это кодировка переменной длины. Для кодирования многих азиатских символов требуется не менее трех байтов. - person Thorbjørn Ravn Andersen; 14.03.2014

Решил эту проблему, сократив строку до требуемой длины байта. Обратите внимание, что этого нельзя сделать, просто используя

stat.substring(0, length)

так как это создает строку UTF-8, которая может быть в три раза длиннее, чем разрешено.

while (stat.getBytes("UTF8").length > length) {
  stat = stat.substring(0, stat.length()-1);
}

обратите внимание, не используйте stat.getBytes (), поскольку он зависит от установленного 'file.encoding' и производит байты либо Windows-1252, либо UTF-8!

Если вы используете Hibernate, вы можете сделать это с помощью org.hibernate.Interceptor!

person dfreis    schedule 15.03.2013