在需要存储较长字符串到数据库中时往往需要使用一些特殊类型的字段,在Oracle中即blob和clob字段,一般而言:Clob字段存储字符信息,比如较长的文字、评论,Blob字段存储字节信息,比如图像的base64编码。
注意,上述字段的使用均可以用其他方式替代,比如用MongoDB或者图片直接存储为文件等等,这里不纠结场景的合适与否,只是针对Blob和Clob类型的使用来举例。
操作场景
主要有三种场景:
- 仅对已知表中的某一字段写入Blob和Clob字段的值
- 更新已知表中全部字段的值(均为Blob和Clob字段)
- 插入数据中带有部分需要插入Blob和Clob字段的数据
总结来看,后两种均以第一种场景为基础,即我们必须明确如何向Blob和Clob字段写入数据。第二种场景实际上是第一种的重复操作,那么对于第三种,需要十分注意,这里意味着需要向表中插入一行记录,操作有部分差异,在此我们就用第三种场景为例来给出示例。
插入时带Blob和Clob字段
情景再现:
从数据源接收数据,解析完成后产生SQL语句并批量插入数据表,注意,原记录中含有若干个Blob字段(图片编码)和若干个Clob字段(记录信息),其余字段均为一般类型(String,Integer)
在给出代码前,注意几点:
- Blob和Clob需要单独处理,即一个SQL语句无法完成上述需求
- 整个过程分为三部分:组装SQL语句、第一遍插入、第二次插入Blob和Clob类型
- 组装SQL语句时:Blob需要人为empty_blob(),置空为Clob需要人为置空为empty_clob()
- 每次插入都需要对特殊字段进行处理,故无法使用batch操作
- 特殊字段处理(第二次插入),必须在第一遍插入之后进行,此时已初始化为empty_blob()或empty_clob()
下面就以带特定场景需求的代码来展示写入示例。
<< 更多精彩尽在『程序萌部落』>>
<< https://www.cxmoe.com >>
代码背景
数据源每次发送一个XML字符串非常长,代码端每次解析这个串,解析后会成为 N 条记录,其中每条记录要解析为 M 个字段,其中含有 m 个Blob字段和 n 个Clob字段,现在需要把这 N 条记录插入到数据表中。
上述的 N,M,n,m 大小均不定且动态变化(已知某些字段是,但这些字段不一定出现),即大小未知。
大致代码流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
while(iter1.hasNext()){
Element e = iter1.next(); Iterator<Element> iter2 = e.elementIterator(); while(iter2.hasNext()){ boolean flag1 = false; boolean flag2 = false; String blobId = "";
Iterator<Element> iter3 = f.elementIterator(); while(iter3.hasNext()){
switch(colname){ case "CLOB字段名1" : case "CLOB字段名2" : case "CLOB字段名N" : { cList.add(colname); cList.add(h.getStringValue()); flag1 = true; break; } case "BLOB字段名1" : case "BLOB字段名2" : case "BLOB字段名N" :{ bList.add(colname); bList.add(h.getStringValue()); flag2 = true; break; } default:{ if( this value is the primary key ){ blobId = this value } strVALUE.append( this valu , ); strNAMES.append( his value , ); } } } strVALUE.deleteCharAt( strVALUE.length() - 1); strNAMES.deleteCharAt( strNAMES.length() - 1); for(int i = 0;i < cList.size(); i=i+2){ strNAMES.append(",\""+cList.get(i)+"\""); strVALUE.append(",empty_clob()"); } for(int i = 0;i < bList.size(); i=i+2){ strNAMES.append(",\""+bList.get(i)+"\""); strVALUE.append(",empty_blob()"); } sqlStr.append("INSERT INTO 表名 ( "+strNAMES+" ) VALUES ( "+strVALUE+" )"); pstmt = con.prepareStatement(sqlStr.toString());
pstmt.executeUpdate(); if(pstmt != null){ pstmt.close(); } if(flag1){ for(int i = 0;i < cList.size(); i=i+2){ pstmt = con.prepareStatement( "SELECT "+cList.get(i)+" FROM 表名 WHERE 表主键 = "+blobId+" for update"); ResultSet rs = pstmt.executeQuery(); Writer outStream = null; if (rs.next()) { oracle.sql.CLOB clob = (oracle.sql.CLOB) rs.getClob(cList.get(i)); outStream = clob.getCharacterOutputStream(); char[] c = cList.get(i+1).toCharArray(); outStream.write(c, 0, c.length); } outStream.flush(); outStream.close(); con.commit(); if(pstmt != null){ pstmt.close(); } } } if(flag2){ for(int i = 0;i < bList.size(); i=i+2){ pstmt = con.prepareStatement( "SELECT "+bList.get(i)+" FROM 表名 WHERE 表主键 = "+blobId+" for update" ); ResultSet rs = pstmt.executeQuery(); OutputStream os = null; if (rs.next()) { oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob(bList.get(i)); os = blob.getBinaryOutputStream(); InputStream is = new ByteArrayInputStream(bList.get(i+1).getBytes()); int b = 0; while ((b = is.read()) != -1) { os.write(b); } } os.flush(); os.close(); con.commit(); if(pstmt != null){ pstmt.close(); } } } } }
|
上述代码段的环境非常特殊,前面已经说了,是一个比较复杂的处理逻辑,代码中有些变量定义没写出来,有些地方也去掉了特定变量换成了文字叙述,所以,上述代码仅仅是为了提供思路,并且包含了一些处理技巧:
- 如何结合XML对象解析构造SQL
- 如何拼接SQL字符串
- 如何暂存特殊类型字段
- 如何在第一次插入时设置empty_blob()
- 如何通过主键值来进行第二次插入
- 如何插入Blob和Clob字段
如果你有更好的方法或者是对该文章有任何的疑问或想法,请在下方留言,我会第一时间回复的!
😒 留下您对该文章的评价 😄