通过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]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值