锁定老帖子 主题:Hibernate 中Blob字段的使用
精华帖 (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。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-01-21
不知道lz 有没有感觉
"直接把大量文字写入字段中而不用进行复杂编码" "所以,使用这种方式处理Blob,一定要注意,不要保存大文件,我觉得最好不要超过1M。" 似乎这个方案还有待斟酌? 是否可以 buffer byte[] |
|
返回顶楼 | |
发表时间:2008-01-21
我觉得无论如何,都最好不要往BLOB中存放大文件。毕竟从数据库中读大文件,是非常缓慢的(想想几百兆的文件从数据库一点一点都到服务器,然后再一点一点response出去)。我觉得大文件还是以文件的方式存放比较好。而且传输的时候,直接给出链接就够了。剩下的事情,由操作系统自己往网卡发就是了。可以大大减轻压力。
Hibernate对JDBC做了很多封装。所以我们从Hibernate得到的CLOB和BLOB其实都是Hibernate自己实现的。而像Oracle之类的一些数据库,他们如果要操作BLOB、CLOB,需要用他们自己实现的对象。而Hibernate中得到的大字段对象是无法转化成数据库特有的大字段对象的。如果非要这么做,只能单独写纯JDBC。那么JDBC的获得、代码耦合、事务难以控制等问题都来了。 至于缓冲,我不知道数据库驱动保存BLOB能否自动缓冲。但是,缺省情况下,是要先把整个文件都读到内存中的。这个可无法缓冲。所以,如果一个人读入一个10M的文件,那么这10M在存入数据库前,都要保留在内存中,100个人的话,就要1G。如果更大,那么内存需要更是恐怖。 |
|
返回顶楼 | |
发表时间:2008-01-22
blob可以放到InputStream,需要时再去读
|
|
返回顶楼 | |
发表时间:2008-01-22
rainy 写道 blob可以放到InputStream,需要时再去读
但是我没找到Hibernate直接映射能这么做的方法。能否给个例子? |
|
返回顶楼 | |
发表时间: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();
|
|
返回顶楼 | |
发表时间: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版本了。 |
|
返回顶楼 | |
发表时间:2008-02-02
我去卓越网看了一下你说的那本书的介绍。在章节目录中有“移植到Hibernate 3 ”这么一章。虽然在简介里没有说明介绍的Hibernate的版本,但是通过这章,可以很明显看出介绍的是Hibernate2。
Hibernate2到Hibernate3可以说是一个飞跃。所以不要把Hibernate2的实践直接套用在3上。请在写代码的时候,自己先在机器上运行一下。 |
|
返回顶楼 | |
浏览 7112 次