Самое эффективное решение для чтения CLOB в String и String в CLOB в Java?

У меня есть большой CLOB (более 32 КБ), который я хочу прочитать в строку, используя StringBuilder. Как мне сделать это наиболее эффективным способом? Я не могу использовать конструктор "int length" для StringBuilder, так как длина моего CLOB больше, чем "int" и требует "длинного" значения.

Я не очень хорошо разбираюсь в классах ввода-вывода Java и хотел бы получить некоторые рекомендации.

Изменить. Я пробовал использовать этот код для clobToString():

private String clobToString(Clob data) {
    StringBuilder sb = new StringBuilder();
    try {
        Reader reader = data.getCharacterStream();
        BufferedReader br = new BufferedReader(reader);

        String line;
        while(null != (line = br.readLine())) {
            sb.append(line);
        }
        br.close();
    } catch (SQLException e) {
        // handle this exception
    } catch (IOException e) {
        // handle this exception
    }
    return sb.toString();
}

person Jonas    schedule 30.01.2010    source источник
comment
Что именно вы хотите сделать после того, как прочитаете CLOB в строку?   -  person Omar Al Kababji    schedule 31.01.2010
comment
Вы имеете в виду CLOB в смысле базы данных или просто большую строку?   -  person skaffman    schedule 31.01.2010
comment
Да, это CLOB из базы данных DB2.   -  person Jonas    schedule 31.01.2010
comment
CLOB содержит большую строку XML, которая будет передана в JAXB.   -  person Jonas    schedule 31.01.2010
comment
Мне интересно, есть ли для этого какие-либо полезные классы в Java NIO.   -  person Jonas    schedule 31.01.2010
comment
Я нашел полезную информацию о CLOB в этом документе: java .sun.com/j2se/1.5.0/docs/guide/jdbc/clob.html Но я не понимаю, как использовать .getSubString(1,len), так как len — это тип long, а не int .   -  person Jonas    schedule 31.01.2010
comment
@Sanoj: Вы говорите, что размер вашего CLOB больше, чем может хранить int - это 4 ГБ строковых данных. Вы в этом уверены?   -  person skaffman    schedule 31.01.2010
comment
Нет, еще нет, но я чувствую, какие проблемы у меня могут быть из-за этого, поэтому я проведу еще несколько тестов. Я получил хороший совет здесь, спасибо.   -  person Jonas    schedule 31.01.2010
comment
более 32 КБ - Вы имеете в виду 32 бит?   -  person Stefan Reich    schedule 04.12.2017


Ответы (10)


Я не могу использовать конструктор "int length" для StringBuilder, так как длина моего CLOB больше, чем int и требует значения long.

Если длина CLOB больше, чем помещается в int, данные CLOB также не помещаются в String. Вам придется использовать потоковый подход для обработки такого большого количества XML-данных.

Если фактическая длина CLOB меньше Integer.MAX_VALUE, просто замените long на int, поставив перед ним (int).

person Barend    schedule 31.01.2010
comment
Действительно, если размер CLOB больше 2^32 байт, у вас большие проблемы. - person skaffman; 31.01.2010
comment
Я бы предложил записать это в файл, если ему нужен весь CLOB для обработки - person Khaled.K; 05.01.2016

Хорошо, я предполагаю общее использование, сначала вам нужно загрузить apache commons, там вы найдете служебный класс с именем IOUtils, у которого есть метод с именем copy();

Теперь решение: получить входной поток вашего объекта CLOB с помощью getAsciiStream() и передать его методу copy().

InputStream in = clobObject.getAsciiStream();
StringWriter w = new StringWriter();
IOUtils.copy(in, w);
String clobAsString = w.toString();
person Omar Al Kababji    schedule 30.01.2010
comment
Спасибо, это выглядит красиво. Но я оставляю вопрос открытым еще немного, потому что я бы предпочел решение, которое использует только стандартную библиотеку. - person Jonas; 31.01.2010
comment
У меня уже загружена библиотека Apache Commons, так что это идеальное решение. Спасибо! - person John Strickler; 02.06.2011
comment
getAsciiStream доставит вам головную боль, если вы используете юникод. (или любые символы, выходящие за пределы ascii) - person TJ Ellis; 29.09.2011
comment
Я изменил InputStream на Reader и clobObject.getAsciiStream() на clobObject.getCharacterStream(), чтобы предотвратить проблемы с кодировкой. - person Dormouse; 11.06.2014

Что случилось с:

clob.getSubString(1, (int) clob.length());

?

Например, Oracle oracle.sql.CLOB выполняет getSubString() на внутреннем char[], который определен в oracle.jdbc.driver.T4CConnection, и только System.arraycopy(), а затем переходит к String... Вы никогда не читаете быстрее, чем System.arraycopy().

ОБНОВЛЕНИЕ Получите драйвер ojdbc6.jar, декомпилируйте CLOB реализацию и изучите, какой случай может быть быстрее, исходя из внутренних знаний.

person gavenkoa    schedule 01.07.2014
comment
Оставляет много символов новой строки в строке. - person Gervase; 16.09.2014
comment
@Gervase Новые строки могут иметь важное значение в XML. В любом случае, вы должны обрезать ненужные пробелы и новые строки, прежде чем сохранять их в БД. - person Florian F; 23.05.2016
comment
Некоторые моменты, которые необходимо прояснить: что произойдет, если clob.length() больше, чем Integer.MAX_VALUE? Какая банка содержит oracle.sql.CLOB? - person Stephan; 26.05.2016
comment
@Стефан Я изучал ojdbc6.jar. Integer.MAX_VALUE — это предел длины массива для JDK Platform 2, а строки содержат символы в массиве. Так что вам не повезло с › 2 GiB CLOB... Попробуйте потоковый подход, потому что вы не можете хранить эти данные с чистой моделью памяти Java (если вы не используете какое-то родное расширение и 64-битную платформу с достаточным объемом системной памяти). - person gavenkoa; 27.05.2016
comment
что случилось? stackoverflow.com/questions/16249238/ ... Я сталкиваюсь с той же проблемой, когда SQL закрывает соединение с данными CLOB на производстве - person Marek Bernád; 08.07.2020
comment
@MarekBernád Хорошо. Я считаю, что у вас проблемы, потому что вы пересекаете границы транзакций/подключений. Проблема громоздких фреймворков заключается в том, что они скрывают управление ресурсами. Если вы в управляемой среде EE получаете доступ к геттеру внутри @Transactional )) Если вы беспокоитесь об эффективности, Hibernate не является хорошей структурой. - person gavenkoa; 08.07.2020

Мой ответ - просто аромат того же самого. Но я проверил это с сериализацией заархивированного содержимого, и это сработало. Так что я могу доверять этому решению, в отличие от того, что было предложено первым (с использованием readLine), потому что оно игнорирует разрывы строк и искажает ввод.

/*********************************************************************************************
 * From CLOB to String
 * @return string representation of clob
 *********************************************************************************************/
private String clobToString(java.sql.Clob data)
{
    final StringBuilder sb = new StringBuilder();

    try
    {
        final Reader         reader = data.getCharacterStream();
        final BufferedReader br     = new BufferedReader(reader);

        int b;
        while(-1 != (b = br.read()))
        {
            sb.append((char)b);
        }

        br.close();
    }
    catch (SQLException e)
    {
        log.error("SQL. Could not convert CLOB to string",e);
        return e.toString();
    }
    catch (IOException e)
    {
        log.error("IO. Could not convert CLOB to string",e);
        return e.toString();
    }

    return sb.toString();
}
person Stan Sokolov    schedule 10.12.2012

Если вам действительно необходимо использовать только стандартные библиотеки, вам просто нужно немного расширить решение Омара. (IOUtils Apache — это, по сути, просто набор удобных методов, которые позволяют сэкономить много кода)

Вы уже можете получить входной поток через clobObject.getAsciiStream()

Вам просто нужно «вручную передать» символы в StringWriter:

InputStream in = clobObject.getAsciiStream();
Reader read = new InputStreamReader(in);
StringWriter write = new StringWriter();

int c = -1;
while ((c = read.read()) != -1)
{
    write.write(c);
}
write.flush();
String s = write.toString();

Имейте в виду, что

  1. Если ваш clob содержит больше символов, чем поместится в строку, это не сработает.
  2. Оберните InputStreamReader и StringWriter в BufferedReader и BufferedWriter соответственно для повышения производительности.
person Edwin Lee    schedule 31.01.2010
comment
Это похоже на код, который я предоставил в своем вопросе, есть ли между ними какие-либо ключевые различия, которых я не вижу? Например, с точки зрения производительности? - person Jonas; 31.01.2010
comment
Ой, я пропустил ваш фрагмент кода! Это чем-то похоже, но имейте в виду, что, просто захватив BufferedReader.readLine(), вы пропустите разрывы строк. - person Edwin Lee; 01.02.2010
comment
Небольшая строка исправления 2 должна быть Reader read = new InputStreamReader(in); - person Vivek; 04.06.2012
comment
Нет нет нет. getAsciiStream() принудительно использует кодировку ASCII и искажает все не-ASCII-символы. Что вы делаете, так это получаете InputStream (байты) из источника символов, а затем немедленно превращаете их обратно в символы, используя случайную (по умолчанию для платформы) кодировку на InputStreamReader. Это избыточная операция, за исключением того факта, что она повреждает данные, отличные от ASCII. Просто читайте напрямую из getCharacterStream() Reader и записывайте в StringWriter. - person Christoffer Hammarström; 20.09.2012

Если вы используете Mule, выполните следующие действия.

Следуйте приведенным ниже шагам.

Включите потоковую передачу в коннекторе, т.е. progressStreaming=2

Приведение типа DB2 вернуло CLOB в java.sql.Clob (IBM поддерживает это приведение типа)

Преобразуйте это в поток символов (иногда поток ASCII может не поддерживать некоторые специальные символы). Таким образом, вы можете использовать getCharacterStream()

Это вернет объект «читатель», который можно преобразовать в «String» с помощью common-io (IOUtils).

Короче говоря, используйте компонент groovy и добавьте код ниже.

clobTest = (java.sql.Clob)payload.field1 
bodyText = clobTest.getCharacterStream() 
targetString = org.apache.commons.io.IOUtils.toString(bodyText)
payload.PAYLOADHEADERS=targetString return payload

Примечание. Здесь я предполагаю, что "payload.field1" содержит данные clob.

Вот и все!

С уважением Навин

person Naveen K Reddy    schedule 13.04.2016

Дружественный вспомогательный метод с использованием apache commons.io

Reader reader = clob.getCharacterStream();
StringWriter writer = new StringWriter();
IOUtils.copy(reader, writer);
String clobContent = writer.toString();
person fl0w    schedule 31.07.2018

public static String readClob(Clob clob) throws SQLException, IOException {
    StringBuilder sb = new StringBuilder((int) clob.length());
    Reader r = clob.getCharacterStream();
    char[] cbuf = new char[2048];
    int n;
    while ((n = r.read(cbuf, 0, cbuf.length)) != -1) {
        sb.append(cbuf, 0, n);
    }
    return sb.toString();
}

Описанный выше подход также очень эффективен.

person Rohit    schedule 22.11.2014

CLOB похож на файлы, вы можете легко читать его части, как это

// read the first 1024 characters
String str = myClob.getSubString(0, 1024);

и вы можете перезаписать его вот так

// overwrite first 1024 chars with first 1024 chars in str
myClob.setString(0, str,0,1024);

Я не предлагаю использовать StringBuilder и заполнять его до тех пор, пока вы не получите исключение, почти как слепое добавление чисел, пока не получите переполнение. Clob похож на текстовый файл, и лучший способ прочитать его — использовать буфер, на случай, если вам нужно его обработать, иначе вы можете передать его в локальный файл, подобный этому.

int s = 0;
File f = new File("out.txt");
FileWriter fw new FileWriter(f);

while (s < myClob.length())
{
    fw.write(myClob.getSubString(0, 1024));
    s += 1024;
}

fw.flush();
fw.close();
person Khaled.K    schedule 05.01.2016

person    schedule
comment
Обычно лучше объяснить решение, чем просто публиковать несколько строк анонимного кода. Вы можете прочитать Как написать хороший ответ, а также Объяснение полностью основанных на коде ответов - person Anh Pham; 04.12.2017