2010-01-30 104 views
30

我有一个很大的CLOB(超过32kB),我想使用StringBuilder读取字符串。我如何以最有效的方式做到这一点?我无法使用StringBuilder的“int length”构造函数,因为CLOB的长度比“int”长,需要一个“long”值。用于将CLOB读入String的最有效解决方案,以及Java中用于CLOB的字符串的最有效解决方案?

我不喜欢Java I/O类,并希望得到一些指导。

编辑 - 我有这个代码受审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(); 
} 
+0

你想一旦你读CLOB为一个字符串到底该怎么做? – 2010-01-30 22:38:34

+0

你是指数据库意义上的CLOB,还是只是“大字符串”? – skaffman 2010-01-30 22:41:45

+0

是的,它是来自DB2数据库的CLOB。 – Jonas 2010-01-30 22:42:49

回答

13

因为我CLOB的长度比int长,需要一个long值,我不能用“INT长度”的构造函数StringBuilder

如果CLOB长度大于适合int的值,那么CLOB数据也不适合String。您必须使用流式方法来处理这么多的XML数据。

如果CLOB的实际长度超过Integer.MAX_VALUE小,只是通过把(int)在它的前面迫使longint

+5

事实上,如果CLOB大小大于2^32字节,那么您遇到了很大的问题 – skaffman 2010-01-31 11:55:01

+0

如果他需要整个CLOB来处理 – 2016-01-05 10:39:26

34

好,我会想一个通用,首先你要下载apache commons,在那里你会找到一个实用工具类名为IOUtils,它有一个名为copy()的方法;

现在解决方案是:使用getAsciiStream()获取CLOB对象的输入流并将其传递给copy()方法。

InputStream in = clobObject.getAsciiStream(); 
StringWriter w = new StringWriter(); 
IOUtils.copy(in, w); 
String clobAsString = w.toString(); 
+0

谢谢,那看起来不错。但是我更关注这个问题,因为我更喜欢只使用标准库的解决方案。 – Jonas 2010-01-31 09:46:57

+0

我已经加载了Apache Commons库,所以这是一个完美的解决方案。谢谢!如果你使用unicode,getAsciiStream会给你带来麻烦。 (或者任何超出ascii的字符) – 2011-06-02 14:47:03

+7

为了防止编码问题,我将'InputStream'改为'Reader','clobObject.getAsciiStream()'改为'clobObject.getCharacterStream()'。 – Dormouse 2014-06-11 07:52:03

2

如果您确实必须只使用标准库,那么您只需要扩展Omar的解决方案。 (Apache的IOUtils基本上是一套方便的方法,从而节省了大量的编码)

你已经能够通过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. 分别用BufferedReader和BufferedWriter包装InputStreamReader和StringWriter以获得更好的性能。
+0

看起来与我在我的代码中提供的代码类似问题,他们之间有没有我看不到的关键区别?在性能角度的例子? – Jonas 2010-01-31 12:41:48

+0

糟糕,我错过了你的代码片段!它有点类似,但请记住,只需抓住BufferedReader.readLine(),就会错过换行。 – 2010-02-01 00:36:32

+1

小修正行2应该是读取器读取= new InputStreamReader(in); – Vivek 2012-06-04 07:15:23

13

我的答案只是一个相同的味道。但我测试了序列化一个压缩的内容,它的工作。所以我可以相信这个解决方案不同于先提供的解决方案(使用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(); 
} 
10

出了什么问题:

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

例如甲骨文oracle.sql.CLOB使内部char[]getSubString()oracle.jdbc.driver.T4CConnection定义,只是System.arraycopy()和明年换到String ...你永远不会得到更快的读取然后System.arraycopy()

UPDATE获取驱动ojdbc6.jar和反编译CLOB落实,并研究这种情况下会更快基于内部知识。

+0

在字符串中留下大量换行符。 – Gervase 2014-09-16 19:58:29

+0

@Gervase Newlines在XML中可能很重要。无论如何,在将它存储到数据库之前,你应该修剪无用的空格和换行符。 – 2016-05-23 14:43:53

+0

需要清楚的几点:如果clob.length()大于Integer.MAX_VALUE,会发生什么情况?什么是jar包含oracle.sql.CLOB? – Stephan 2016-05-26 14:33:24

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

上述方法也非常有效。

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; 
} 
-1

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

,以下是步骤。

请按照以下步骤操作。

启用流在所述连接器即progressiveStreaming = 2

类型转换DB2返回到CLOB java.sql.Clob中(IBM支持这种类型的铸造)

转换,要字符流(ASCII流有时可能不支持一些特殊字符)。所以你可以使用getCharacterStream()

这将返回一个“reader”对象,它可以使用common-io(IOUtils)转换为“String”。

因此总之,使用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数据。

就是这样!

问候纳文

0
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(); 
} 
+0

通常最好解释一个解决方案,而不是只发布一些匿名代码行。你可以阅读[我如何写一个好的答案](https://stackoverflow.com/help/how-to-answer),还有[完全解释基于代码的答案](https://meta.stackexchange.com /问题/ 114762 /解释-entirely-%E2%80%8C%E2%80%8Bcode为主,答案) – 2017-12-04 10:32:23