[关闭]
@EVA001 2018-09-01T16:35:09.000000Z 字数 3667 阅读 517

Java向Oracle数据库表中的CLOB、BLOB字段写入数据

未分类


前言

在需要存储较长字符串到数据库中时往往需要使用一些特殊类型的字段,在Oracle中即blob和clob字段,一般而言:

注意,上述字段的使用均可以用其他方式替代,比如用MongoDB或者图片直接存储为文件等等,这里不纠结场景的合适与否,只是针对Blob和Clob类型的使用来举例。

操作场景

主要有三种场景:

总结来看,后两种均以第一种场景为基础,即我们必须明确如何向Blob和Clob字段写入数据。第二种场景实际上是第一种的重复操作,那么对于第三种,需要十分注意,这里意味着需要向表中插入一行记录,操作有部分差异,在此我们就用第三种场景为例来给出示例。

插入时带Blob和Clob字段

情景再现:

从数据源接收数据,解析完成后产生SQL语句并批量插入数据表,注意,原记录中含有若干个Blob字段(图片编码)和若干个Clob字段(记录信息),其余字段均为一般类型(String,Integer)

在给出代码前,注意几点:

  1. Blob和Clob需要单独处理,即一个SQL语句无法完成上述需求
  2. 整个过程分为三部分:组装SQL语句、第一遍插入、第二次插入Blob和Clob类型
  3. 组装SQL语句时:Blob需要人为empty_blob(),置空为Clob需要人为置空为empty_clob()
  4. 每次插入都需要对特殊字段进行处理,故无法使用batch操作
  5. 特殊字段处理(第二次插入),必须在第一遍插入之后进行,此时已初始化为empty_blob()或empty_clob()

下面就以带特定场景需求的代码来展示写入示例。

代码背景

数据源每次发送一个XML字符串非常长,代码端每次解析这个串,解析后会成为 N 条记录,其中每条记录要解析为 M 个字段,其中含有 m 个Blob字段和 n 个Clob字段,现在需要把这 N 条记录插入到数据表中。

上述的 N,M,n,m 大小均不定且动态变化(已知某些字段是,但这些字段不一定出现),即大小未知。

大致代码流程

  1. // ... ... 整个过程围绕xml节点的迭代来完成
  2. while(iter1.hasNext()){
  3. Element e = iter1.next();
  4. Iterator<Element> iter2 = e.elementIterator();
  5. // 每一条SQL
  6. while(iter2.hasNext()){
  7. boolean flag1 = false; // 标志是否含有Clob字段
  8. boolean flag2 = false; // 标志是否含有Blob字段
  9. String blobId = ""; // 储存所在SQL语句的主键值
  10. // ... ...
  11. // 开始组装每一条SQL语句
  12. Iterator<Element> iter3 = f.elementIterator();
  13. while(iter3.hasNext()){
  14. // ... ...
  15. switch(colname){
  16. case "CLOB字段名1" :
  17. case "CLOB字段名2" :
  18. // ...
  19. case "CLOB字段名N" : { //暂存CLOB数据
  20. cList.add(colname);
  21. cList.add(h.getStringValue());
  22. flag1 = true;
  23. break; // break switch
  24. }
  25. case "BLOB字段名1" :
  26. case "BLOB字段名2" :
  27. // ...
  28. case "BLOB字段名N" :{ //暂存BLOB数据
  29. bList.add(colname);
  30. bList.add(h.getStringValue());
  31. flag2 = true;
  32. break;
  33. }
  34. default:{
  35. if( this value is the primary key ){
  36. blobId = this value
  37. }
  38. strVALUE.append( this valu , ); // 字段值
  39. strNAMES.append( his value , ); // 字段名
  40. }
  41. }
  42. }
  43. // 去掉最后一个多余的逗号
  44. strVALUE.deleteCharAt( strVALUE.length() - 1);
  45. strNAMES.deleteCharAt( strNAMES.length() - 1);
  46. // 然后追加处理 empty_clob()和empty_blob()
  47. for(int i = 0;i < cList.size(); i=i+2){
  48. strNAMES.append(",\""+cList.get(i)+"\"");
  49. strVALUE.append(",empty_clob()");
  50. }
  51. for(int i = 0;i < bList.size(); i=i+2){
  52. strNAMES.append(",\""+bList.get(i)+"\"");
  53. strVALUE.append(",empty_blob()");
  54. }
  55. // 最终形态(第一次插入的语句)
  56. sqlStr.append("INSERT INTO 表名 ( "+strNAMES+" ) VALUES ( "+strVALUE+" )");
  57. pstmt = con.prepareStatement(sqlStr.toString());
  58. pstmt.executeUpdate(); // first insert done
  59. if(pstmt != null){
  60. pstmt.close();
  61. }
  62. // 上述第一次插入完成后,开始单独处理特殊类型(第二次插入)
  63. // 根据 flag1 判断是否有Clob类型的数据
  64. if(flag1){
  65. for(int i = 0;i < cList.size(); i=i+2){
  66. pstmt = con.prepareStatement(
  67. "SELECT "+cList.get(i)+" FROM 表名 WHERE 表主键 = "+blobId+" for update");
  68. ResultSet rs = pstmt.executeQuery();
  69. Writer outStream = null;
  70. if (rs.next()) {
  71. //得到java.sql.Clob对象后强制转换为oracle.sql.CLOB
  72. oracle.sql.CLOB clob = (oracle.sql.CLOB) rs.getClob(cList.get(i));
  73. outStream = clob.getCharacterOutputStream();
  74. //传入字符串
  75. char[] c = cList.get(i+1).toCharArray();
  76. outStream.write(c, 0, c.length);
  77. }
  78. outStream.flush();
  79. outStream.close();
  80. con.commit();
  81. if(pstmt != null){
  82. pstmt.close();
  83. }
  84. }
  85. }
  86. // 根据 flag1 判断是否有Blob类型的数据
  87. if(flag2){
  88. for(int i = 0;i < bList.size(); i=i+2){
  89. pstmt = con.prepareStatement(
  90. "SELECT "+bList.get(i)+" FROM 表名 WHERE 表主键 = "+blobId+" for update" );
  91. ResultSet rs = pstmt.executeQuery();
  92. OutputStream os = null;
  93. if (rs.next()) {
  94. // 得到java.sql.Blob对象后强制转换为oracle.sql.BLOB
  95. oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob(bList.get(i));
  96. // 通过getBinaryOutputStream()方法获得向数据库中插入图片的流
  97. os = blob.getBinaryOutputStream();
  98. // 读取想要存储的图片文件(或串值)
  99. InputStream is = new ByteArrayInputStream(bList.get(i+1).getBytes());
  100. // 依次读取流字节,并输出到已定义好的数据库字段中.
  101. int b = 0;
  102. while ((b = is.read()) != -1) {
  103. os.write(b);
  104. }
  105. }
  106. os.flush();
  107. os.close();
  108. con.commit();
  109. if(pstmt != null){
  110. pstmt.close();
  111. }
  112. }
  113. }
  114. } // end while
  115. } // end while

上述代码段的环境非常特殊,前面已经说了,是一个比较复杂的处理逻辑,代码中有些变量定义没写出来,有些地方也去掉了特定变量换成了文字叙述,所以,上述代码仅仅是为了提供思路,并且包含了一些处理技巧:

如果你有更好的方法或者是对该文章有任何的疑问或想法,请在下方留言,我会第一时间回复的!

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注