在
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
字段的映射类型。(好了等到下一篇再说吧!)