發信人: dmoracle (dmoracle), 信區: Java 標 題: 通過JDBC訪問Oracle的Lob類型的方法(原創) 發信站: 武漢白雲黃鶴站 (2003年07月25日09:48:16 星期五), 站內信件
LOBs("Large Objects")採用優化空間和提供高效訪問的方式來管理的。Oracle JDBC支持兩種LOBS:BLOBs(無結構的二進制數據)和CLOBs(字符數據).BLOB和CLOB數據都是通過一個定位符(Locator)來進行訪問的。這個定位符其實就是一個指針。它存放在數據庫的表中,指向位於該表之外 的BLOB和CLOB數據。 爲了操作LOBs類型的列,首先必須獲得該類型的定位符,通過該定位符來進行操作。Oracle.sql.BLOB和Oracle.sql.CLOB分別實現了java.sql.Blob和java.sql.Clob接口。在Java應用中,不能直接生成一個LOB對象。只有通過檢索數據庫中的數據或者利用createTemporary()和empty_l ob()來構造Lob對象。 1.操作LOB定位符 利用JDBC標準或者Oracle所提供的擴展方法可以獲取或者設置LOB類型的定位符。這說明Oracle的JDBC同Oracle數據庫之間都是通過定位符來進行操作多媒體數據的。 (1)檢索LOB定位符: 在包含了LOB類型的結果集或者CallableStatement對象中,可以通過標準(1.2)的getBlob()或者getClob()來獲得指向LOB數據的定位符。也可以利用(1.1)getObject()來獲取。 如果把結果集或者CallableStatement塑造成OracleResultSet或者OracleCallableStatement對象,就可以使用Oracle的擴展方法(1.2)getBLOB()、getCLOB()或者(1.1)getOracleObject()來獲取。 例子: (i)從結果集中獲得LOB對象的定位符
// Select LOB locator into standard result set. ResultSet rs = stmt.executeQuery ("SELECT blob_col, clob_col FROM lob_table"); while (rs.next()) { // Get LOB locators into Java wrapper classes. java.sql.Blob blob = (java.sql.Blob)rs.getObject(1); java.sql.Clob clob = (java.sql.Clob)rs.getObject(2); (...process...) }
也可以把結果塑造成oracle.sql.LOB類型: // Get LOB locators into Java wrapper classes. oracle.sql.BLOB blob = (BLOB)rs.getObject(1); oracle.sql.CLOB clob = (CLOB)rs.getObject(2); (...process...)
(ii)從CallableStatement的結果中獲得LOB對象的定位符,它的操作方法同結果集基本上一樣:
OracleCallableStatement ocs = (OracleCallableStatement)conn.prepareCall("{? = call func()}"); ocs.registerOutParameter(1, OracleTypes.CLOB); ocs.execute(); oracle.sql.CLOB clob = ocs.getCLOB(1);
(2)設置LOB定位符 在包含LOB類型的PreparedStatement或者CallableStatement對象中,可以使用標準的方法:(1.2)setBlob()、setClob()或者(1.1)setObject()來設置相應的列或者參數的值。也可以塑造成OraclePreparedStatement或者OracleCallableStatement來使用(1.2)setBLOB()、setC LOB()或者(1.1)setOracleObject()來進行操作。 例子:
(i)爲PreparedStatement設置參數的值: OraclePreparedStatement ops = (OraclePreparedStatement)conn.prepareStatement ("INSERT INTO blob_table VALUES(?)"); ops.setBLOB(1, my_blob); ops.execute();
(ii)爲CallableStatement設置參數的值: OracleCallableStatement ocs = (OracleCallableStatement)conn.prepareCall("{call proc(?))}"); ocs.setClob(1, my_clob); ocs.execute();
2.操作LOB類型的數據 一旦獲得了LOB對象的定位符,就可以利用JDBC的方法來訪問LOB對象的數據了。LOB數據可以構造成Java數組或者數據流。但是這個數據流同其他Java數據流不同,因爲代表LOB數據的定位符是存放在表中的,我們在同數據連接期間隨時都可以訪問LOB數據。 oracle.sql.BLOB和oracle.sql.CLOB提供了訪問LOB數據的方法。利用這些方法,客戶端可以把數據讀入到數據流中,或者從數據流中取數據到LOB中,也可以查詢LOB數據的長度,還可以關閉LOB對象。 在往LOB類型中寫入數據的時候,一定要使用寫入鎖定,這可以通過select for update來實現,同時設置爲非自動提交模式。 操作LOB數據,可以使用下列方法: (a)讀取BLOB數據:使用oracle.sql.BLOB的getBinaryStream()方法,返回一個java.io.InputStream對象,利用它的read方法就可以讀取BLOB的數據了。最後利用InputStream的close()方法關閉。 (b)寫入BLOB數據:使用oracle.sql.BLOB的getBinaryOutputStream()方法,返回一個java.io.OutputStream對象,利用它的write方法往BLOB中寫入數據。最後利用OutputStream的close()方法關閉。 (c)讀取CLOB數據:使用oracle.sql.CLOB的getAsciiStream()方法或者getCharacterStream()方法。前者返回一個Ascii碼的InputStream,後者返回一個Unicode碼的Reader。也可以使用getSubString()來來返回一個String對象。 (d)寫入CLOB數據:使用oracle.sql.CLOB的getAsciiOutputStream()方法或者getCharacterOutputStream()方法。前者返回一個Ascii碼的OutputStream,後者返回一個Unicode碼的Writer。在使用了write方法寫入數據之後,要調用flush()和close()方法來關閉。
注意: (i)在往LOB中寫入數據的時候,是直接寫到數據庫中了。這一切對於客戶端來說都是透明的,不需要再進行Update操作。但是,在寫入之後,需要對事務進行提交操作才能真正保存到數據庫中。 (ii)在操作數據的時候,JDBC自動實現字符集之間的轉換。 (iii)JDBC 2.0規範指出,PreparedStatement的setBinaryStream()和setObject()方法可以用來往BLOB列輸入數據;PreparedStatement的setAsciiStream()、setUnicodeStream()、setCharacterStream()以及setObject()都可以用來往CLOB列輸入數據。這幾個方法繞過了LOB定位 符,直接訪問LOB數據本身。在Oracle JDBC驅動程序的實現中,這個功能僅僅在8.1.6以及更高版本的數據庫的一個配置中得到支持,或者使用較高版本的JDBC OCI驅動程序。
例子: (1) // Read BLOB data from BLOB locator. InputStream byte_stream = my_blob.getBinaryStream(); byte [] byte_array = new byte [10]; int bytes_read = byte_stream.read(byte_array);
(2) // Read CLOB data from CLOB locator into Reader char stream. Reader char_stream = my_clob.getCharacterStream(); char [] char_array = new char [10]; int chars_read = char_stream.read (char_array, 0, 10);
(3) Inputstream asciiChar_stream = my_clob.getAsciiStream(); byte[] asciiChar_array = new byte[10]; int asciiChar_read = asciiChar_stream.read(asciiChar_array,0,10);
(4) java.io.OutputStream outstream; // read data into a byte array byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; // write the array of binary data to a BLOB outstream = ((BLOB)my_blob).getBinaryOutputStream(); outstream.write(data);
(5) java.io.Writer writer; // read data into a character array char[] data = {'0','1','2','3','4','5','6','7','8','9'}; // write the array of character data to a CLOB writer = ((CLOB)my_clob).getCharacterOutputStream(); writer.write(data); writer.flush(); writer.close();
(6) java.io.OutputStream out; // read data into a byte array byte[] data = {'0','1','2','3','4','5','6','7','8','9'}; // write the array of ascii data to a CLOB out = ((CLOB)clob).getAsciiOutputStream(); out.write(data); out.flush(); out.close();
3.操作一個LOB列的過程: (1)新建一個包含LOB列的表: String cmd = "create table my_blob_table(x varchar2(30),c blob);"; stmt.executeUpdate(cmd); (2)產生BLOB實體,也就是爲其生成一個定位符: stmt.executeUpdate("insert into my_blob_table values('row1',empty_blob());"); (3)獲得BLOB定位符: BLOB blob; cmd = "SELECT * FROM my_blob_table WHERE X=’row1’"; ResultSet rset = stmt.executeQuery(cmd); rset.next(); BLOB blob = ((OracleResultSet)rset).getBLOB(2); (4)指定要存放到數據庫中的數據: File binaryFile = new File("john.gif"); System.out.println("john.gif length = " + binaryFile.length()); FileInputStream instream = new FileInputStream(binaryFile); OutputStream outstream = blob.getBinaryOutputStream(); (5)獲得理想的緩衝區: int size = blob.getBufferSize(); byte[] buffer = new byte[size]; int length = -1; (6)把數據讀入緩衝區,然後寫入數據庫: while ((length = instream.read(buffer)) != -1) outstream.write(buffer, 0, length); instream.close(); outstream.close(); (7)數據寫入數據庫之後,就可以讀取了: // Select the blob - what we are really doing here // is getting the blob locator into a result set BLOB blob; cmd = "SELECT * FROM my_blob_table"; ResultSet rset = stmt.executeQuery (cmd); // Get the blob data - cast to OracleResult set to // retrieve the data in oracle.sql format String index = ((OracleResultSet)rset).getString(1); blob = ((OracleResultSet)rset).getBLOB(2); // get the length of the blob int length = blob.length(); // print the length of the blob System.out.println("blob length" + length); // read the blob into a byte array // then print the blob from the array byte bytes[] = blob.getBytes(1, length); printBytes(bytes, length);
可以看出,LOB在對其進行操作之前一定要進行初始化,也就是說,爲其分配一個定位符。利用empty_lob()就可以給LOB指定一個特定的標記。這個時候還不能讀取其中的內容,因爲它沒有定位符,只是一個空指針而已。如果讀取的話,就會拋出異常。
4.打開/關閉LOB 用戶不必進行打開、關閉LOBs的操作。如果用戶考慮的性能的原因,可能也會執意這麼做。如果用戶不在打開和關閉操作之間進行LOB操作的話,那麼對LOB的每次改變都會隱含的打開、關閉LOBs,就會觸動與LOB有關的觸發器。LOB的內容稍有改變都會及時的反映出來。 如果用戶在打開和關閉操作之間進行LOB操作的話,那麼LOB的改變直到關閉之後纔會觸發與之相關的觸發器。 用戶可以調用open()或者open(int mode)方法來打開LOBs。在關閉之前,所有的讀寫操作都不會觸動與之相關的觸發器。可以使用isOpen()來判斷一個LOB是否已經關閉。mode參數只能爲MODE_READONLY和MODE_READWRITE兩個之一。這兩個值在oracle.sql.BLOB和CLOB中定義。從字面上 就可以看出每個模式下的所侷限的操作。如果在MODE_READONLY下進行寫操作的話,就會拋出SQL異常。
在進行事務提交之前,一定要關閉LOBs。不然的話就會出錯:系統自己把LOBs關閉,而且事務成功進行提交。但是與之有關的任何觸發器都不會執行,這會影響到數據的完整性,甚至會導致系統的崩潰。
5.一些問題的探討 (1)同Oracle7偏重於LONG、RAW相比,Oracle8i更偏重於LOB: (a)LOB可以存放4G大小的數據,是LONG的2倍; (b)單張表可以有多個LOB類型列,而只能有一個LONG列; (c)LOB採用隨機讀取,LONG採用順序讀取;而且LOB採用分塊的形式傳送。 (2)LOB不僅僅只能存放Locator,它還可以直接存放小於4K的數據;如果超過4K,數據庫自動把它從表中取出,放到別的段,甚至是別的數據庫空間。原來的值用Locator來代替。 (3)在複製LOB列的時候(insert、update操作),複製的不僅僅是Locator,還包括LOB的數據. (4)如果用戶所用的字符集是變長的,那麼數據庫採用Unicode字符集來統一存儲。 (5)不管LOB的數據存放在數據庫的那個部分,表中一定存有它的Locator。Locator可以看作是一個指向LOB數據真實位置的指針。對於某一行的LOB列都有唯一的Locator。 (6)一般都是把LOB初始化爲empty,也可以用小於4k的數據來初始化它。 (7)能否在LOB列存放小於4K的數據,可以通過命令DISABLE STORAGE來指定。
6.總結 Oracle的Long、Raw這兩種類型同DM3的Text、Image相對應,其存放的方式、存取的方式都是類似的。 Oracle的CLOB和BLOB具有自己獨特之處,從文獻上看Oracle比較推薦使用這兩種類型。 -- ※ 來源:·武漢白雲黃鶴站 bbs.whnet.edu.cn·[FROM: 202.114.1.108]
|