Вопрос по java, clob, stringbuilder – Наиболее эффективное решение для чтения CLOB в String и String в CLOB в Java?

37

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

Я не очень доволен классами ввода / вывода 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();
}
«больше, чем 32 КБ» - Вы имеете в виду 32 бит? Stefan Reich
Вы имеете в виду CLOB в смысле базы данных или просто "большая строка"? skaffman
Нет, пока нет, но у меня есть чувство, какие проблемы у меня могут возникнуть, поэтому я проведу еще несколько тестов. Я получил хороший совет, спасибо. Jonas
Мне интересно, есть ли полезные классы в Java NIO для этого. Jonas

Ваш Ответ

11   ответов
1
public static final String tryClob2String(final Object value)
{
    final Clob clobValue = (Clob) value;
    String result = null;

    try
    {
        final long clobLength = clobValue.length();

        if (clobLength < Integer.MIN_VALUE || clobLength > Integer.MAX_VALUE)
        {
            log.debug("CLOB size too big for String!");
        }
        else
        {
            result = clobValue.getSubString(1, (int) clobValue.length());
        }
    }
    catch (SQLException e)
    {
        log.error("tryClob2String ERROR: {}", e);
    }
    finally
    {
        if (clobValue != null)
        {
            try
            {
                clobValue.free();
            }
            catch (SQLException e)
            {
                log.error("CLOB FREE ERROR: {}", e);
            }
        }
    }

    return result;
}
4

и, то вам просто нужно немного расширить решение Омара. (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();

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

Если ваш clob содержит больше символов, чем поместится в строку, это не сработает.Оберните InputStreamReader и StringWriter с BufferedReader и BufferedWriter соответственно для лучшей производительности.
Строка малой коррекции 2 должна быть Reader read = new InputStreamReader (in); Vivek
Это похоже на код, который я предоставил в своем вопросе, есть ли какие-то ключевые различия между ними, которых я не вижу? В примере с точки зрения производительности? Jonas
Нет нет нет.getAsciiStream() форсирует ASCII-кодирование и повреждает все не-ASCII-символы. То, что вы делаете, это получитьInputStream (байты) из источника символов, а затем немедленно превращать их обратно в символы, используя случайную кодировку (платформа по умолчанию) дляInputStreamReader, Это избыточная операция, за исключением того факта, что она портит данные не ASCII. Просто прочитайте изgetCharacterStream() Reader прямо и напишитеStringWriter. Christoffer Hammarström
К сожалению, я пропустил ваш фрагмент кода! Это несколько похоже, но имейте в виду, что, просто взяв BufferedReader.readLine (), вы пропустите разрывы строк. Edwin Lee
1
private String convertToString(java.sql.Clob data)
{
    final StringBuilder builder= new StringBuilder();

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

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

        br.close();
    }
    catch (SQLException e)
    {
        log.error("Within SQLException, Could not convert CLOB to string",e);
        return e.toString();
    }
    catch (IOException e)
    {
        log.error("Within IOException, Could not convert CLOB to string",e);
        return e.toString();
    }
    //enter code here
    return builder.toString();
}
Обычно лучше объяснить решение, а не просто опубликовать несколько строк анонимного кода. Ты можешь читатьКак мне написать хороший ответ, а такжеОбъясняя полностью основанные на коде ответы Anh Pham
17

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

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

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

Я бы предложил записать его в файл, если ему нужен весь CLOB для обработки Khaled.K
Действительно, если размер CLOB больше 2 ^ 32 байт, у вас большие проблемы skaffman
0

использующий apache commons.io

Reader reader = clob.getCharacterStream();
StringWriter writer = new StringWriter();
IOUtils.copy(reader, writer);
String clobContent = writer.toString();
18

ацией заархивированного содержания, и это работало. Поэтому я могу доверять этому решению в отличие от предложенного первым (использующего 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();
}
41

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

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

InputStream in = clobObject.getAsciiStream();
StringWriter w = new StringWriter();
IOUtils.copy(in, w);
String clobAsString = w.toString();
Я изменилсяInputStream вReader а такжеclobObject.getAsciiStream() вclobObject.getCharacterStream() чтобы предотвратить проблемы кодирования. Dormouse
У меня уже есть библиотека Apache Commons, так что это идеальное решение. Спасибо! John Strickler
Просто и идеально! спасибо Омар Cristian B.
getAsciiStream даст вам головную боль, если вы используете Unicode. (или любые символы, выходящие за пределы ascii) TJ Ellis
Спасибо, это выглядит мило. Но я оставлю вопрос открытым чуть больше, потому что предпочел бы решение, которое использует только стандартную библиотеку. Jonas
15

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

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

?

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

ОБНОВИТЬ Получить драйверojdbc6.j, ари декомпилироватьCLOB реализации, и изучить, какой случай будет быстрее на основе внутренних знаний.

Некоторые моменты, которые необходимо прояснить: что произойдет, если clob.length () больше, чем Integer.MAX_VALUE? Что в банке содержится oracle.sql.CLOB? Stephan
@Gervase Новые строки могут иметь большое значение в XML. В любом случае, вы должны обрезать бесполезные пробелы и переводы строк перед сохранением в БД. Florian F
@ Стефан, которого я изучалojdbc6.jar. Integer.MAX_VALUE предел длины массива дляJDK Platform 2 и String содержат символы в массиве. Так что вам не повезло с> 2 GiB CLOB ... Попробуйте потоковый подход, потому что вы не можете хранить эти данные с чистой моделью памяти Java (если вы не используете какое-то собственное расширение и 64-битную платформу с достаточным объемом системной памяти). gavenkoa
Оставляет много символов новой строки в строке. Gervase
0
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();
}

3

Выполните следующие шаги.

Включить потоковую передачу в соединителе, т.е.

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

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

Это вернет объект «reader», который можно преобразовать в «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.

Это оно!

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

-1

его части можно легко прочитать, как это

// 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();

Похожие вопросы