在
Java
中,
java.lang.String
可用於表示長字符串(長度超過
255
),字節數組
byte[]
能夠用於存放圖片戶或文件二進制數據。此外,在
JDBC API
中還提供了
java.sql.CLOB
和
java.sql.BLOB
類型,他們分別表示標準
SQL
中的
CLOB
(字符大對象)和
BLOB
(二進制大對象)類型。表
2.4
列出了
Java
大對象,
Hibernate
映射類型以及標準
SQL
的對應關係。
表
2.4
映射類型
|
Java
類型
|
標準
SQL
類型
|
binary
|
byte[]
|
VARBINARY(
或者
BLOB)
|
text
|
java.lang.String
|
CLOB
|
serilizable
|
實現
java.io.Serializable
的任何一個
Java
類
|
VARBINARY(
或者
BLOB)
|
clob
|
java.sql.CLOB
|
CLOB
|
blob
|
java.sql.BLOB
|
BLOB
|
注意:不容許用表
2.4
中列出的數據類型來定義持久化類的
OID
(
2
)、
BLOB,CLOB
數據的處理(以
Oracle
數據庫爲例):
假設咱們有以下表
T_User
|
id number <pk>
name varchar2(50)
age number
image BLOB
resume CLOB
|
對應的映射文件以下:
<hibernate-mapping>
<class name=」com.neusoft.hibernate.db.entity.TUser」 table=」T_User」>
<id name=」id」 column=」id」 type=」java.lang.Integer」>
<generator class=」native」/>
</id>
<property name=」name」 type=」java.lang.String」 column=」name」/>
<property name=」age」 type=」java.lang.Integer」 column=」age」/>
<property name=」image」 type=」java.sql.Blob」 column=」image」/>
<property name=」resume」 type=」java.sql.Clob」 column=」resume」/>
</class>
</hibernate-mapping>
實體類以下:
public class Tuser implements Serializable{
private Integer id;
private String name;
private Integer age;
private Blob image;
private Clob resume;
…getter/setter…..
}
對
BLOB
和
CLOB
這種大對象通常都是採用流機制做爲數據讀取方式,因此這種讀取方式在
Oracle
這種自視爲數據庫中貴族的數據庫中對這種操做就會有諸多的限制,有時候會叫人以爲不太友好(這是典型的店大欺客)
限制一:
Oracle JDBC
不容許流操做以批量方式執行,若是發生這種錯誤通常會拋出
ERROR JDBCExceptionReport:streams type cannot be used in batching.
出現這種狀況通常都須要將
hibernate.cfg.xml
中的
hibernate.jdbc.batch_size
設定爲
0
便可消除,可是這就會影響其餘的更新,插入,刪除操做的性能,所以必須在一個數據庫事務中對
Clob,Blob
進行操做,也只有在一個數據庫事務中
Clob,Blob
對象纔會有效。
限制二:
Oalce Blob/Clob
具備獨特的訪問方式,這種類型字段擁有一個遊標
(cursor)
,
JDBC
必須經過遊標對
Blob/Clob
進行操做,在
Blob/Clob
建立以前我門沒法得到其遊標句炳,這就意味着必須首先建立一個空
Blob/Clob
字段,在叢空
Blob/Clob
獲取遊標,而後寫入咱們指望的數據。
首先看一下采用傳統
JDBC
進行操做的代碼:
//….
獲取
Connection
鏈接
conn.setAtuoCommit(false);
//
插入
Blob/Clob
空值字段
PrepareStatement prestmt=conn.prepareStatement(「insert into T_USER(name,age,id,image,resume) values(?,?,?,?,?)」);
prestmt.setString(1,」zx」);
prestmt.setInt(2,26);
prestmt.setInt(3,5);
//
經過
oracle.sql.BLOB/CLOB.empty_lob()
方法構造空
Blob/Clob
對象
prestmt.setBlob(4,oracle.sql.BLOB.empty_lob());
prestmt.setClob(5,oracle.sql.CLOB.empty_lob());
prestmt.executeUpdate();
prestmt.close();
//
再次從數據庫中得到
Blob/Clob
句炳
prestmt=conn.prepareStatement(「select image,resume from T_USER where id=? for update 」);
prestmt.setInt(1,5);
ResultSet rset=prestmt.executeQuery();
rset.next();
oracle.sql.BLOB imgBlob=(oracle.sql.BLOB)rset.getBlob();
oracle.sql.CLOB resClob=(oracle.sql.CLOB)rset.getClob();
//
將二進制數據寫入
Blob
FlieInputStream fin=new FileInputStream(「c://image.jpg」);
OutputStream out=imgBlob.getBinaryOutputStream();
byte[] buf=new byte[fin.available()];
int len;
while((len=fin.read(buf))!=-1){
out.write(buf,0,len);
}
fin.close();
out.close();
//
將字符串寫入
Clob
resClob.putString(1,」This is my clob」);
//
將更新寫回數據庫
prestmt=conn.prepareStatement(「update T_USER set image=?,resume=? where id=? 」);
prestmt.setBlob(1,imgBlob);
prestmt.setClob(2,resClob);
prestmt.setInt(3,5);
prestmt.executeUpdate();
prestmt.close();
conn.commit();
conn.close();
以上是傳統的採用
JDBC
方式處理,注意他將鏈接的自動提交屬性設置爲
false
而後將全部的操做併入一個事務中,而後進行提交,這是處理
Oracle
中
Blob/Clob
字段的通常機制,因此
Hibernate
的處理就應該模仿
JDBC
的處理方式,由於從某種角度來說
Hibernate
是對
JDBC
的封裝由於它的底層訪問機制仍然是基於
JDBC
的。
Hibernate
的處理:
TUser user=new TUser();
user.setAge(new Integer(26));
user.setName(「zx」);
//
建立空
Blob/Clob
對象
user.setImage(Hibernate.createBlob(new byte[1]));
user.setResume(Hibernate.createClob(「 「));//
注意這裏的參數是一個空格
Transaction tx=session.beginTransaction();
session.save(user);
//
調用
flush
方法,強制
Hibernate
當即執行
insert sql
session.flush();
//
經過
refresh
方法,強制
Hibernate
執行
select for update
session.refresh(user,LockMode.UPGRADE);
//
向
Blob
寫入實際內容
oracle.sql.BLOB blob=(oracle.sql.BLOB)user.getImger();
OutputStream out=blob.getBinaryOutputStream();
FileInputStream fin=new FileInputStream(「c://image.jpg」);
byte[] buf=new byte[fin.available()];
int len;
while((len=fin.read())!=-1){
out.write(buf,0,len);
}
fin.close();
out.close();
//
向
Clob
中寫入數據
oracle.sql.CLOB clob=user.getResume();
java.io.Writer writer=clob.getCharacterOutputStream();
writer.write(「This is my resume!」);
writer.close();
session.saveOrUpdate(user);
session.commit();
tx.commit();
在實際應用中,對於
Clob
字段能夠簡單的將其映射爲
String
類型,不過在
Oracle Thin Driver
對
Clob
字段支持上有欠缺,當
Clob
內容超過
4000
字節時將沒法讀取,而
Oracle OCI Driver(
須要在本地安裝客戶端組件
)
則能夠完成大容量
Clob
字段操做。
對於上面的代碼相信做爲成熟的工程師來講都聞到一些
bad smell,
若是
Blob/Clob
字段廣泛存在的話,那麼咱們的持久層邏輯可能遍及這種複雜的邏輯,不過不要着急在我即將講解的客戶自定義類中咱們將會看到一個解決方案,經過自定義類型咱們能夠對數據類型的通用性進行抽象,對於
Blob/Clob
字段咱們能夠定義一種類型並以這種類型做爲
Blob/Clob
字段的映射類型。(好了等到下一篇再說吧!)