论坛首页 Java企业应用论坛

Hibernate 中Blob字段的使用

浏览 7112 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2008-01-21  
猫咪以前发了一个关于在Hibernate中使用Clob字段的文章。可以做到以String的方式,直接把大量文字写入字段中而不用进行复杂编码。现在猫咪把使用Blob的方法也写出来。猫咪很懒,所以很久没有更新博客了。大家多多包涵!
下面是猫咪的例子(仍然使用Oracle,请确保使用最新的JDBC驱动)
首先建立一个数据库表

create table textblob (tid number(38,0),lob blob)

然后建立对应的Java对象

public class testblob {
         private long id;
         private byte[] lob;
         //set和get方法略...
    }

 最后是Hibernate配置和映射文件。

<hibernate-configuration>
        <session-factory>
        <property name="connection.driver_class">
            oracle.jdbc.driver.OracleDriver
            </property>
            <property name="connection.url">
            jdbc:oracle:thin:@xxx.xxx.xxx.xxx:1521:orcl
            </property>
            <property name="connection.useUnicode">true</property>
            <property name="connection.characterEncoding">UTF-8</property>
            <property name="connection.username">xxxxxx</property>
            <property name="connection.password">xxxxxx</property>
            <property name="hibernate.connection.provider_class">
            org.hibernate.connection.C3P0ConnectionProvider
            </property>
            <property name="hibernate.c3p0.max_size">20</property>
            <property name="hibernate.c3p0.min_size">5</property>
            <property name="hibernate.c3p0.timeout">120</property>
            <property name="hibernate.c3p0.max_statements">100</property>
            <property name="hibernate.c3p0.idle_test_period">120</property>
            <property name="hibernate.c3p0.acquire_increment">2</property>
            <property name="hibernate.connection.SetBigStringTryClob">
            true
            </property>
            <property name="hibernate.jdbc.use_streams_for_binary">
            true
            </property>
            <property name="dialect">
            org.hibernate.dialect.Oracle9Dialect
            </property>
            <property name="hibernate.show_sql">true</property>
            <property name="hibernate.transaction.factory_class">
            org.hibernate.transaction.JDBCTransactionFactory
            </property>

      <mapping resource="org/Miao/testblob.hbm.xml" />
         </session-factory>
     </hibernate-configuration>

 

 

注意配置文件中“hibernate.jdbc.use_streams_for_binary”这个部分。这是Oracle必须添加的属性。其它配置没有什么特别的。SQL Server不需要这个属性。同时配置文件配置了一个C3P0数据库连接池。

 

映射文件如下:

<hibernate-mapping package="org.Miao">
        <class name="testblob" table="tblob">
            <id name="id" column="tid">
               <generator class="native"></generator>
            </id>
        <property name="lob" column="tlob" type="binary">

    </property>
     </class>
</hibernate-mapping>

 

然后就可以测试一下了。写一个运行的测试类Run.class。

public class run {
      public static void main(String[] args) {
         Configuration configuration = new Configuration();
         configuration.configure();
         SessionFactory sessionFactory = configuration.buildSessionFactory();
         Session session = sessionFactory.openSession();
         File f = new File("d:/f.exe");
         byte[] tmp = new byte[(int) f.length()];
         try {
               FileInputStream fi = new FileInputStream(f);
               fi.read(tmp);
          } catch (FileNotFoundException e1) {
          e1.printStackTrace();
          } catch (IOException e) {
          e.printStackTrace();
          }
          testblob test = new testblob();
          test.setId(5);
          test.setLob(tmp);
          Transaction t = session.beginTransaction();
          try {
              session.saveOrUpdate(test);
              t.commit();
          } catch (HibernateException e) {
             e.printStackTrace();
             t.rollback();
          } finally {
             session.close();
          }
    }

}

 

现在进入数据库的话,可以看到数据库记录中已经增加了一条,文件已经被保存到数据库里了。但是,这么做有一个很大的问题,那就是内存。在Eclipse+WTP的环境下,加入的文件稍微大一点,就会造成OutofMemony异常。因为用这种方法存入数据库,需要把文件全部读入内存才可以。文件一大,就会造成内存溢出,即使把JVM使用的内存调大,保存大文件也是非常慢的。我的调试环境,存一个10M的文件,单用户Tomcat(内存调整到256-512M)跑,Oracle10g又是远程,需要好几分钟。多用户条件下,性能可想而知。而且,一旦多用户同时保存或同时读取大文件,立马会造成内存占用暴增,最后溢出。所以,使用这种方式处理Blob,一定要注意,不要保存大文件,我觉得最好不要超过1M。

   发表时间:2008-01-21  
不知道lz 有没有感觉
"直接把大量文字写入字段中而不用进行复杂编码"
"所以,使用这种方式处理Blob,一定要注意,不要保存大文件,我觉得最好不要超过1M。"

似乎这个方案还有待斟酌?

是否可以 buffer byte[]
0 请登录后投票
   发表时间:2008-01-21  
我觉得无论如何,都最好不要往BLOB中存放大文件。毕竟从数据库中读大文件,是非常缓慢的(想想几百兆的文件从数据库一点一点都到服务器,然后再一点一点response出去)。我觉得大文件还是以文件的方式存放比较好。而且传输的时候,直接给出链接就够了。剩下的事情,由操作系统自己往网卡发就是了。可以大大减轻压力。
Hibernate对JDBC做了很多封装。所以我们从Hibernate得到的CLOB和BLOB其实都是Hibernate自己实现的。而像Oracle之类的一些数据库,他们如果要操作BLOB、CLOB,需要用他们自己实现的对象。而Hibernate中得到的大字段对象是无法转化成数据库特有的大字段对象的。如果非要这么做,只能单独写纯JDBC。那么JDBC的获得、代码耦合、事务难以控制等问题都来了。
至于缓冲,我不知道数据库驱动保存BLOB能否自动缓冲。但是,缺省情况下,是要先把整个文件都读到内存中的。这个可无法缓冲。所以,如果一个人读入一个10M的文件,那么这10M在存入数据库前,都要保留在内存中,100个人的话,就要1G。如果更大,那么内存需要更是恐怖。
0 请登录后投票
   发表时间:2008-01-22  
blob可以放到InputStream,需要时再去读
0 请登录后投票
   发表时间:2008-01-22  
rainy 写道
blob可以放到InputStream,需要时再去读

但是我没找到Hibernate直接映射能这么做的方法。能否给个例子?
0 请登录后投票
   发表时间:2008-02-02  
根据深入浅出hibernate这本书.
把你的实现修改一下.

 

<property name="lob" column="tlob" type="java.sql.Blob">  

然后要在hibernate配置文件中加入一句话:

<property name="hibernate.jdbc.batch_size">0</property>  

 

因为Oracle JBDC不允许流操作以批量方式执行.

然后

测试类里面可以写:

testblob test = new testblob();   
test.setId(5);   
test.setLob(Hibernate.createBlob(new byte[1]));  
Transaction t = session.beginTransaction();   
session.save(test);   
//调用flush方法,强制Hibernate立即执行 sql 语句
session.flush();
//通过refresh方法,强制Hibernate执行select for update
session.refresh(test,LockMode.UPGRADE);
//向Blob写入实际内容
oracle.sql.BLOB blob=(oracle.sql.BLOB)test.getLob();
OutPutStream out =blob.getBinaryOutputStream();
FileInputStream imgis=new FileInputStream("xx.jpg");
byte[] buf = new byte[10240];//10k缓存
int len=0;
while((len=imgis.read(buf))>0){
out.write(buf,0,len);
}
imgis.close();
out.close();
session.save(test);
t.comit();

 

0 请登录后投票
   发表时间:2008-02-02  
dmewy 写道
根据深入浅出hibernate这本书.
把你的实现修改一下.

 

<property name="lob" column="tlob" type="java.sql.Blob">  

然后要在hibernate配置文件中加入一句话:

<property name="hibernate.jdbc.batch_size">0</property>  

 

因为Oracle JBDC不允许流操作以批量方式执行.

然后

测试类里面可以写:

testblob test = new testblob();   
test.setId(5);   
test.setLob(Hibernate.createBlob(new byte[1]));  
Transaction t = session.beginTransaction();   
session.save(test);   
//调用flush方法,强制Hibernate立即执行 sql 语句
session.flush();
//通过refresh方法,强制Hibernate执行select for update
session.refresh(test,LockMode.UPGRADE);
//向Blob写入实际内容
oracle.sql.BLOB blob=(oracle.sql.BLOB)test.getLob();
OutPutStream out =blob.getBinaryOutputStream();
FileInputStream imgis=new FileInputStream("xx.jpg");
byte[] buf = new byte[10240];//10k缓存
int len=0;
while((len=imgis.read(buf))>0){
out.write(buf,0,len);
}
imgis.close();
out.close();
session.save(test);
t.comit();

 


很可惜。这么写是错误的。

Hibernate.createBlob(new byte[1]))生成的BLOB根本无法转化成Oracle的BLOB。它们虽然都继承自BLOB,但是不能强行转换。这种写法只适用于Hibernate2,3以后是不能用的。如果不想用我的方法,那么只能自己写Hibernate类型或使用Spring提供的类型。但是这样一来增加了难度或和Spring紧耦合了。

还有,你的写法造成了代码和数据库的紧耦合。也就是说如果更换数据库,那么必须修改代码。同时在Hibernate配置中关闭了批处理,造成系统整个性能下降。可以说是得不偿失。

深入浅出Hibernate我记得应该是一本比较早期的Hibernate参考书,上面的很多东西已经不适用于最新的Hibernate版本了。

0 请登录后投票
   发表时间:2008-02-02  
我去卓越网看了一下你说的那本书的介绍。在章节目录中有“移植到Hibernate 3 ”这么一章。虽然在简介里没有说明介绍的Hibernate的版本,但是通过这章,可以很明显看出介绍的是Hibernate2。
Hibernate2到Hibernate3可以说是一个飞跃。所以不要把Hibernate2的实践直接套用在3上。请在写代码的时候,自己先在机器上运行一下。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics