在Oracle中,LOB(Large Object,大型對象)類型的字段如今用得愈來愈多了。由於這種類型的字段,容量大(最多能容納4GB的數據),且一個表中能夠有多個這種類型的字段,很靈活,適用於數據 量很是大的業務領域(如圖象、檔案等)。html
LOB類型分爲BLOB和CLOB兩種:BLOB即二進制大型對象(Binary Large Object),適用於存貯非文本的字節流數據(如程序、圖象、影音等)。而CLOB,即字符型大型對象(Character Large Object),則與字符集相關,適於存貯文本型的數據(如歷史檔案、大部頭著做等)。java
建表SQL語句爲:sql
CREATE TABLE TEST_CLOB ( ID NUMBER(3), CLOBCOL CLOB) CREATE TABLE TEST_BLOB ( ID NUMBER(3), BLOBCOL BLOB)
oracleDb_Driver=oracle.jdbc.driver.OracleDriver oracleDb_Url=jdbc:oracle:thin:@localhost:1521:GACL oracleDb_UserName=GACL_XDP oracleDb_Password=P
package me.gacl.utils; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; public class JdbcUtils { private static String oracleDb_Driver = null; private static String oracleDb_Url = null; private static String oracleDb_UserName = null; private static String oracleDb_Password = null; static{ try{ //讀取db.properties文件中的數據庫鏈接信息 InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties"); Properties prop = new Properties(); prop.load(in); //獲取數據庫鏈接驅動 oracleDb_Driver = prop.getProperty("oracleDb_Driver"); //獲取數據庫鏈接URL地址 oracleDb_Url = prop.getProperty("oracleDb_Url"); //獲取數據庫鏈接用戶名 oracleDb_UserName = prop.getProperty("oracleDb_UserName"); //獲取數據庫鏈接密碼 oracleDb_Password = prop.getProperty("oracleDb_Password"); //加載數據庫驅動 Class.forName(oracleDb_Driver); }catch (Exception e) { throw new ExceptionInInitializerError(e); } } /** * @Method: getOracleConnection * @Description: 獲取Oracle數據庫鏈接對象 * @Anthor:孤傲蒼狼 * * @return Connection數據庫鏈接對象 * @throws SQLException */ public static Connection getOracleConnection() throws SQLException{ return DriverManager.getConnection(oracleDb_Url, oracleDb_UserName,oracleDb_Password); } /** * @Method: release * @Description: 釋放資源, * 要釋放的資源包括Connection數據庫鏈接對象,負責執行SQL命令的Statement對象,存儲查詢結果的ResultSet對象 * @Anthor:孤傲蒼狼 * * @param conn * @param st * @param rs */ public static void release(Connection conn,Statement st,ResultSet rs){ if(rs!=null){ try{ //關閉存儲查詢結果的ResultSet對象 rs.close(); }catch (Exception e) { e.printStackTrace(); } rs = null; } if(st!=null){ try{ //關閉負責執行SQL命令的Statement對象 st.close(); }catch (Exception e) { e.printStackTrace(); } } if(conn!=null){ try{ //關閉Connection數據庫鏈接對象 conn.close(); }catch (Exception e) { e.printStackTrace(); } } } }
package me.gacl.demo; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import org.junit.Test; import me.gacl.utils.JdbcUtils; /** * @ClassName: JdbcOperaOracleClob * @Description:Oracle中字符型大型對象(Character Large Object)數據處理 * @author: 孤傲蒼狼 * @date: 2014-10-7 下午3:53:19 * */ public class JdbcOperaOracleClob { /** CREATE TABLE TEST_CLOB ( ID NUMBER(3), CLOBCOL CLOB) */ /** * @Method: clobInsert * @Description:往數據庫中插入一個新的CLOB對象 * @Anthor:孤傲蒼狼 * * @throws Exception */ @Test public void clobInsert() throws Exception { Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; conn = JdbcUtils.getOracleConnection(); boolean defaultCommit = conn.getAutoCommit(); /*開啓事務,設定不自動提交 */ conn.setAutoCommit(false); try { /* 插入一個空的CLOB對象 */ String sql = "INSERT INTO TEST_CLOB VALUES (?, EMPTY_CLOB())"; stmt = conn.prepareStatement(sql); stmt.setInt(1, 1); stmt.executeUpdate(); /* 查詢此CLOB對象並鎖定 */ sql = "SELECT CLOBCOL FROM TEST_CLOB WHERE ID=? FOR UPDATE"; stmt = conn.prepareStatement(sql); stmt.setInt(1, 1); rs = stmt.executeQuery(); if (rs.next()) { /* 取出此CLOB對象 */ oracle.sql.CLOB clob = (oracle.sql.CLOB) rs.getClob("CLOBCOL"); /* 向CLOB對象中寫入數據 */ BufferedWriter out = new BufferedWriter(clob.getCharacterOutputStream()); //這種方式獲取的路徑,其中的空格會被使用「%20」代替 String path = JdbcOperaClob.class.getClassLoader().getResource("data.txt").getPath(); //將「%20」替換回空格 path = path.replaceAll("%20", " "); BufferedReader in = new BufferedReader(new FileReader(path)); int c; while ((c = in.read()) != -1) { out.write(c); } in.close(); out.close(); } /* 正式提交 */ conn.commit(); System.out.println("插入成功"); } catch (Exception ex) { /* 出錯回滾 */ conn.rollback(); throw ex; }finally{ /* 恢復原提交狀態 */ conn.setAutoCommit(defaultCommit); JdbcUtils.release(conn,stmt,rs); } } /** * @Method: clobRead * @Description: CLOB對象讀取 * @Anthor:孤傲蒼狼 * * @throws Exception */ @Test public void clobRead() throws Exception { Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; conn = JdbcUtils.getOracleConnection(); boolean defaultCommit = conn.getAutoCommit(); conn.setAutoCommit(false); try { /* 查詢CLOB對象 */ String sql = "SELECT * FROM TEST_CLOB WHERE ID=?"; stmt = conn.prepareStatement(sql); stmt.setInt(1, 1); rs = stmt.executeQuery(); if (rs.next()) { /* 獲取CLOB對象 */ oracle.sql.CLOB clob = (oracle.sql.CLOB) rs.getClob("CLOBCOL"); /* 以字符形式輸出 */ BufferedReader in = new BufferedReader(clob.getCharacterStream()); BufferedWriter out = new BufferedWriter(new FileWriter("D:\\2.txt")); int c; while ((c = in.read()) != -1) { out.write(c); } out.close(); in.close(); } } catch (Exception ex) { conn.rollback(); throw ex; }finally{ /* 恢復原提交狀態 */ conn.setAutoCommit(defaultCommit); JdbcUtils.release(conn,stmt,rs); } } /** * @Method: clobModify * @Description:修改CLOB對象(是在原CLOB對象基礎上進行覆蓋式的修改) * @Anthor:孤傲蒼狼 * * @throws Exception */ @Test public void clobModify() throws Exception { Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; conn = JdbcUtils.getOracleConnection(); boolean defaultCommit = conn.getAutoCommit(); // 開啓事務 conn.setAutoCommit(false); try { /* 查詢CLOB對象並鎖定 */ String sql = "SELECT CLOBCOL FROM TEST_CLOB WHERE ID=? FOR UPDATE"; stmt = conn.prepareStatement(sql); stmt.setInt(1, 1); rs = stmt.executeQuery(); if (rs.next()) { /* 獲取此CLOB對象 */ oracle.sql.CLOB clob = (oracle.sql.CLOB) rs.getClob("CLOBCOL"); /* 進行覆蓋式修改 */ BufferedWriter out = new BufferedWriter(clob.getCharacterOutputStream()); // 這種方式獲取的路徑,其中的空格會被使用「%20」代替 String path = JdbcOperaClob.class.getClassLoader().getResource("data2.txt").getPath(); // 將「%20」替換回空格 path = path.replaceAll("%20", " "); BufferedReader in = new BufferedReader(new FileReader(path)); int c; while ((c = in.read()) != -1) { out.write(c); } in.close(); out.close(); } /*提交事務 */ conn.commit(); } catch (Exception ex) { /*出錯回滾事務 */ conn.rollback(); throw ex; }finally{ /*恢復原提交狀態 */ conn.setAutoCommit(defaultCommit); JdbcUtils.release(conn,stmt,rs); } } /** * @Method: clobReplace * @Description:替換CLOB對象(將原CLOB對象清除,換成一個全新的CLOB對象) * @Anthor:孤傲蒼狼 * * @throws Exception */ @Test public void clobReplace() throws Exception { Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; conn = JdbcUtils.getOracleConnection(); boolean defaultCommit = conn.getAutoCommit(); // 開啓事務 conn.setAutoCommit(false); try { /* 清空原CLOB對象 */ String sql = "UPDATE TEST_CLOB SET CLOBCOL=EMPTY_CLOB() WHERE ID=?"; stmt = conn.prepareStatement(sql); stmt.setInt(1, 1); stmt.executeUpdate(); /* 查詢CLOB對象並鎖定 */ sql = "SELECT CLOBCOL FROM TEST_CLOB WHERE ID=? FOR UPDATE"; stmt = conn.prepareStatement(sql); stmt.setInt(1, 1); rs = stmt.executeQuery(); if (rs.next()) { /* 獲取此CLOB對象 */ oracle.sql.CLOB clob = (oracle.sql.CLOB) rs.getClob("CLOBCOL"); /* 更新數據 */ BufferedWriter out = new BufferedWriter(clob.getCharacterOutputStream()); // 這種方式獲取的路徑,其中的空格會被使用「%20」代替 String path = JdbcOperaClob.class.getClassLoader().getResource("db.properties").getPath(); // 將「%20」替換回空格 path = path.replaceAll("%20", " "); BufferedReader in = new BufferedReader(new FileReader(path)); int c; while ((c = in.read()) != -1) { out.write(c); } in.close(); out.close(); } /* 正式提交 */ conn.commit(); } catch (Exception ex) { /* 出錯回滾 */ conn.rollback(); throw ex; } finally { /* 恢復原提交狀態 */ conn.setAutoCommit(defaultCommit); JdbcUtils.release(conn, stmt, rs); } } }
Oracle定義了一個BLOB字段用於保存二進制數據,但這個字段並不能存放真正的二進制數據,只能向這個字段存一個指針,而後把數據放到指針所指向的Oracle的LOB段中, LOB段是在數據庫內部表的一部分。於是在操做Oracle的Blob以前,必須得到指針(定位器)才能進行Blob數據的讀取和寫入。
如何得到表中的Blob指針呢? 能夠先使用insert語句向表中插入一個空的blob(調用oracle的函數empty_blob()),這將建立一個blob的指針,而後再把這個empty的blob的指針查詢出來,這樣就可獲得BLOB對象,從而讀寫blob數據了。數據庫
一、插入空blob:insert into testblob(id,image) values(?,empty_blob())
二、得到blob的cursor:架構
select image from testblob where id=? for update 注意: 必 須加for update鎖定該行,直至該行被修改完畢,保證不產生併發衝突。併發
Blob b = rs.getBlob("image");
三、利用 io和獲取到的cursor往數據庫讀寫數據
注意:以上操做需開啓事務。oracle
BLOB對象的存取範例函數
package me.gacl.demo; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import org.junit.Test; import me.gacl.utils.JdbcUtils; /** * @ClassName: JdbcOperaOracleBlob * @Description:Oracle中大數據處理 * @author: 孤傲蒼狼 * @date: 2014-10-7 下午3:53:19 * */ public class JdbcOperaOracleBlob { /** * @Method: blobInsert * @Description: 向數據庫中插入一個新的BLOB對象 * @Anthor:孤傲蒼狼 * * @throws Exception */ @Test public void blobInsert() throws Exception { Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; boolean defaultCommit = true; try { conn = JdbcUtils.getOracleConnection(); //獲得數據庫事務處理的默認提交方式 defaultCommit = conn.getAutoCommit(); //一、開啓事務 conn.setAutoCommit(false); //二、插入一個空的BLOB對象 String sql = "INSERT INTO TEST_BLOB VALUES (?, EMPTY_BLOB())"; stmt = conn.prepareStatement(sql); stmt.setInt(1, 1); stmt.executeUpdate(); //三、查詢此BLOB對象並鎖定。注意: 必 須加for update鎖定該行,直至該行被修改完畢,保證不產生併發衝突 sql = "SELECT BLOBCOL FROM TEST_BLOB WHERE ID=? FOR UPDATE"; stmt = conn.prepareStatement(sql); stmt.setInt(1, 1); rs = stmt.executeQuery(); if (rs.next()) { //四、取出此BLOB對象 ,並強制轉換成Oracle的BLOB對象 oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob("BLOBCOL"); //五、使用IO向BLOB對象中寫入數據 BufferedOutputStream out = new BufferedOutputStream(blob.getBinaryOutputStream()); BufferedInputStream in = new BufferedInputStream(JdbcOperaOracleBlob.class.getClassLoader().getResourceAsStream("01.jpg")); int c; while ((c = in.read()) != -1) { out.write(c); } in.close(); out.close(); } //六、提交事務 conn.commit(); } catch (Exception ex) { //七、出錯回滾事務 conn.rollback(); throw ex; } finally { //八、恢復數據庫事務處理的默認提交方式 conn.setAutoCommit(defaultCommit); //釋放資源 JdbcUtils.release(conn, stmt, rs); } } /** * @Method: blobModify * @Description:修改BLOB對象(是在原BLOB對象基礎上進行覆蓋式的修改) * @Anthor:孤傲蒼狼 * * @throws Exception */ @Test public void blobModify() throws Exception { Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; boolean defaultCommit = true; try { conn = JdbcUtils.getOracleConnection(); //獲得數據庫事務處理的默認提交方式 defaultCommit = conn.getAutoCommit(); //一、開啓事務 conn.setAutoCommit(false); //二、查詢此BLOB對象並鎖定。注意: 必 須加for update鎖定該行,直至該行被修改完畢,保證不產生併發衝突 String sql = "SELECT BLOBCOL FROM TEST_BLOB WHERE ID=? FOR UPDATE"; stmt = conn.prepareStatement(sql); stmt.setInt(1, 1); rs = stmt.executeQuery(); if (rs.next()) { //三、取出此BLOB對象 ,並強制轉換成Oracle的BLOB對象 oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob("BLOBCOL"); //四、使用IO向BLOB對象中寫入數據 BufferedOutputStream out = new BufferedOutputStream(blob.getBinaryOutputStream()); BufferedInputStream in = new BufferedInputStream(JdbcOperaOracleBlob.class.getClassLoader().getResourceAsStream("02.jpg")); int c; while ((c = in.read()) != -1) { out.write(c); } in.close(); out.close(); } //五、提交事務 conn.commit(); } catch (Exception ex) { //六、出錯回滾事務 conn.rollback(); throw ex; } finally { //八、恢復數據庫事務處理的默認提交方式 conn.setAutoCommit(defaultCommit); //釋放資源 JdbcUtils.release(conn, stmt, rs); } } /** * @Method: blobReplace * @Description:替換BLOB對象(將原BLOB對象清除,換成一個全新的BLOB對象) * @Anthor:孤傲蒼狼 * * @throws Exception */ @Test public void blobReplace() throws Exception { Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; boolean defaultCommit = true; try { conn = JdbcUtils.getOracleConnection(); //獲得數據庫事務處理的默認提交方式 defaultCommit = conn.getAutoCommit(); //一、開啓事務 conn.setAutoCommit(false); //二、清空原BLOB對象 String sql = "UPDATE TEST_BLOB SET BLOBCOL=EMPTY_BLOB() WHERE ID=?"; stmt = conn.prepareStatement(sql); stmt.setInt(1, 1); stmt.executeUpdate(); //三、查詢此BLOB對象並鎖定。注意: 必 須加for update鎖定該行,直至該行被修改完畢,保證不產生併發衝突 sql = "SELECT BLOBCOL FROM TEST_BLOB WHERE ID=? FOR UPDATE"; stmt = conn.prepareStatement(sql); stmt.setInt(1, 1); rs = stmt.executeQuery(); if (rs.next()) { //四、取出此BLOB對象 ,並強制轉換成Oracle的BLOB對象 oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob("BLOBCOL"); //五、使用IO向BLOB對象中寫入數據 BufferedOutputStream out = new BufferedOutputStream(blob.getBinaryOutputStream()); BufferedInputStream in = new BufferedInputStream(JdbcOperaOracleBlob.class.getClassLoader().getResourceAsStream("01.jpg")); int c; while ((c = in.read()) != -1) { out.write(c); } in.close(); out.close(); } //六、提交事務 conn.commit(); } catch (Exception ex) { //七、出錯回滾事務 conn.rollback(); throw ex; } finally { //八、恢復數據庫事務處理的默認提交方式 conn.setAutoCommit(defaultCommit); //釋放資源 JdbcUtils.release(conn, stmt, rs); } } /** * @Method: blobRead * @Description:BLOB對象讀取 * @Anthor:孤傲蒼狼 * * @throws Exception */ @Test public void blobRead() throws Exception { Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; boolean defaultCommit = true; try { conn = JdbcUtils.getOracleConnection(); //獲得數據庫事務處理的默認提交方式 defaultCommit = conn.getAutoCommit(); //一、開啓事務 conn.setAutoCommit(false); //二、查詢BLOB對象 String sql = "SELECT BLOBCOL FROM TEST_BLOB WHERE ID=?"; stmt = conn.prepareStatement(sql); stmt.setInt(1, 1); rs = stmt.executeQuery(); if (rs.next()) { //三、取出此BLOB對象 ,並強制轉換成Oracle的BLOB對象 oracle.sql.BLOB blob = (oracle.sql.BLOB) rs.getBlob("BLOBCOL"); //四、以二進制流的形式輸出 BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("D:/1.jpg")); BufferedInputStream in = new BufferedInputStream(blob.getBinaryStream()); int c; while ((c = in.read()) != -1) { out.write(c); } in.close(); out.close(); } //五、提交事務 conn.commit(); } catch (Exception ex) { //六、出錯回滾事務 conn.rollback(); throw ex; } finally { //八、恢復數據庫事務處理的默認提交方式 conn.setAutoCommit(defaultCommit); //釋放資源 JdbcUtils.release(conn, stmt, rs); } } }
經過JDBC操縱Oracle數據庫的LOB字段,不外乎插入、修改、替換、讀取四種方式,掌握起來並不難。觀察上述程序對LOB類型字段的存取,咱們能夠看出,較之其它類型字段,有下面幾個顯著不一樣的特色:工具
一、必須取消自動提交。測試
存取操做開始前,必須用setAutoCommit(false)取消自動提交。其它類型字段則無此特殊要求。這是由於存取LOB類型字段時,一般要進行屢次操做能夠完成。不這樣的話,Oracle將拋出「讀取違反順序」的錯誤。
二、插入方式不一樣。
LOB數據不能象其它類型數據同樣直接插入(INSERT)。插入前必須先插入一個空的LOB對象,CLOB類型 的空對象爲EMPTY_CLOB(),BLOB類型的空對象爲EMPTY_BLOB()。以後經過SELECT命令查詢獲得先前插入的記錄並鎖定,繼而將 空對象修改成所要插入的LOB對象。
三、修改方式不一樣。
其它類型的字段修改時,用UPDATE … SET…命令便可。而LOB類型字段,則只能用SELECT … FOR UPDATE命令將記錄查詢出來並鎖定,而後才能修改。且修改也有兩種改法:一是在原數據基礎上的修改(即覆蓋式修改),執行SELECT … FOR UPDATE後再改數據;二是替換(先將原數據清掉,再修改),先執行UPDATE命令將LOB字段之值設爲空的LOB對象,而後進行第一種改法。建議使 用替換的方法,以實現與其它字段UPDATE操做後同樣的效果。
四、存取時應使用由數據庫JDBC驅動程序提供的LOB操做類。
對於Oracle數據庫,應使用oracle.sql.CLOB和oracle.sql.BLOB。不使用由數據庫JDBC驅動程序提供的LOB類時,程序運行時易於出現「抽象方法調用」的錯誤,這是由於JDBC所定義的java.sql.Clob與 java.sql.Blob接口,其中的一些方法並未在數據庫廠家提供的驅動程序中真正實現。
五、存取手段與文件操做相仿。
對於BLOB類型,應用InputStream/OutputStream類,此類不進行編碼轉換,逐個字節存取。oracle.sql.BLOB類相應提供了getBinaryStream()和getBinaryOutputStream()兩個方法,前一個 方法用於讀取Oracle的BLOB字段,後一個方法用於將數據寫入Oracle的BLOB字段。
對於CLOB類型,應用Reader/Writer類,此類進行編碼轉換。oracle.sql.CLOB類相應 提供了getCharacterStream()和getCharacterOutputStream()兩個方法,前一個方法用於讀取Oracle的 CLOB字段,後一個方法用於將數據寫入Oracle的CLOB字段。
須要說明的是,爲了大幅提升程序執行效率,對BLOB/CLOB字段的讀寫操做,應該使用緩衝操做類(帶 Buffered前綴),即:BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter。 例程中所有使用了緩衝操做類。