Java向Oracle數據庫表中插入CLOB、BLOB字段

小編:啊南 19閱讀 2020.12.02

在需要存儲較長字符串到數據庫中時往往需要使用一些特殊類型的字段,在Oracle中即blob和clob字段,一般而言:Clob字段存儲字符信息,比如較長的文字、評論,Blob字段存儲字節信息,比如圖像的base64編碼。

注意,上述字段的使用均可以用其他方式替代,比如用MongoDB或者圖片直接存儲為文件等等,這里不糾結場景的合適與否,只是針對Blob和Clob類型的使用來舉例。

操作場景

主要有三種場景:

  • 僅對已知表中的某一字段寫入Blob和Clob字段的值
  • 更新已知表中全部字段的值(均為Blob和Clob字段)
  • 插入數據中帶有部分需要插入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 大小均不定且動態變化(已知某些字段是,但這些字段不一定出現),即大小未知。

大致代碼流程
// ... ... 整個過程圍繞xml節點的迭代來完成
while(iter1.hasNext()){  
  Element e = iter1.next();  
  Iterator<Element> iter2 = e.elementIterator(); 
    
  // 每一條SQL
  while(iter2.hasNext()){   
    	
    boolean flag1 = false; // 標志是否含有Clob字段
    boolean flag2 = false; // 標志是否含有Blob字段
    
    String blobId = "";	// 儲存所在SQL語句的主鍵值
    
    // ... ...
    // 開始組裝每一條SQL語句
    Iterator<Element> iter3 = f.elementIterator();  
    while(iter3.hasNext()){  
 
      // ... ...
      switch(colname){
	  
	    case "CLOB字段名1" :
	    case "CLOB字段名2" :	
	    // ... 	
	    case "CLOB字段名N" : { //暫存CLOB數據 
	      cList.add(colname);
	      cList.add(h.getStringValue());
	      flag1 = true;
	      break; // break switch
	    }
	    case "BLOB字段名1" :
	    case "BLOB字段名2" :
	    // ...
	    case "BLOB字段名N" :{ //暫存BLOB數據 
	      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);
    	
    // 然后追加處理 empty_clob()和empty_blob()
    	
    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(); // first insert done
        
    if(pstmt != null){
      pstmt.close();
    }
        	
    // 上述第一次插入完成后,開始單獨處理特殊類型(第二次插入)
        
    // 根據 flag1 判斷是否有Clob類型的數據
    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()) {  
          //得到java.sql.Clob對象后強制轉換為oracle.sql.CLOB  
          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();
        }
      }
    }
        
    // 根據 flag1 判斷是否有Blob類型的數據
    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()) {  
          // 得到java.sql.Blob對象后強制轉換為oracle.sql.BLOB  
          oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob(bList.get(i));  
          // 通過getBinaryOutputStream()方法獲得向數據庫中插入圖片的流
          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();
        }
      }
    }
  } // end while 
} // end while

上述代碼段的環境非常特殊,前面已經說了,是一個比較復雜的處理邏輯,代碼中有些變量定義沒寫出來,有些地方也去掉了特定變量換成了文字敘述,所以,上述代碼僅僅是為了提供思路,并且包含了一些處理技巧:

  • 如何結合XML對象解析構造SQL
  • 如何拼接SQL字符串
  • 如何暫存特殊類型字段
  • 如何在第一次插入時設置empty_blob()
  • 如何通過主鍵值來進行第二次插入
  • 如何插入Blob和Clob字段
關聯標簽:
华东15选5彩票奖结果 北京快3发车时间 武汉麻将技巧十句口诀 河内五分彩大神计划 七星彩和值走势图200期 腾讯棋牌60游戏 哪个平台可以玩澳洲幸运10 福建时时彩网络代销怎么做 广东快乐十分最快开奖查询 五子棋游戏 贵州快三走基本走势图 二分彩是国家开奖吗 以太坊交易速率限制的原因 乐透乐博彩论坛一Welcome 九肖中特公式规律 新时时彩历史数据 - 点击进入 排球比分直播网站