通過JDBC訪問Oracle的Lob類型的方法(來自武漢白雲黃鶴站)

 


發信人: 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]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章