posts - 6, comments - 8, trackbacks - 0, articles - 6
            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          作為測(cè)試,我們新建一個(gè)TUser對(duì)象,其image屬性中,保存了一個(gè)圖片文件的二進(jìn)制內(nèi)容。而其resume屬性,我們以一個(gè)簡(jiǎn)單的字符串作為填充。

          ?

          ?1 TUser?user? = new ?TUser();?
          ?2
          ?3 user.setAge( new ?Integer( 20 ));?
          ?4
          ?5 user.setName( " Shark " );?
          ?6
          ?7 FilelnputStream?imgis? = new ?FileinputStream( " C:\\inimage.jpg " ?
          ?8
          ?9 Blob?img? = ?Hibernate.createBlob(imgis);?
          10
          11 user.setlmage(img);?
          12
          13 Clob?resume? = ?Hibernate.createClob( " This?is?Clob " );?
          14
          15 user.?setResume(resume);?
          16
          17 Transaction?tx? = ?session.beginTransaction();?
          18
          19 session.save(user);?
          20
          21 tx.commit();?
          22
          23

          上面的代碼中,我們通過(guò)Hibemate.createBlobHibemate.createClob創(chuàng)建了對(duì)應(yīng)的BlobClob對(duì)象。其中Blob對(duì)象基于一個(gè)FileInputStream構(gòu)建,而Clob對(duì)象基于一個(gè)字符串構(gòu)建。

          ?

          完成了寫入操作,對(duì)應(yīng)的讀取操作代碼如下:

          ?

          ?1 // ?假設(shè)庫(kù)表記錄的id字段等于3?
          ?2 TUser?user = (TUser)??session.load(TUger.elaa.,?load(TUser. class ,?? new ?Integer( 3 ));?
          ?3 Clob?resume = user.getResume();?
          ?4 // ?通過(guò)Clob.getSubString()方法獲取Clob字段內(nèi)容?
          ?5 System.out.println( " User?resume=> " + resume.getSubString( 1 ,( int )resume.length()));?
          ?6 Blob?img? = ?user.getImage();?
          ?7 // ?通過(guò)Blob.getBinaryS=ream()方法獲取二進(jìn)制流?
          ?8 InputStream?is? = ?img.getBinaryStream();?
          ?9 FileOutputStream?fos = new ?FileOutputStream( " C:\\outimage.jpg " );?
          10 byte []?buf = new ? byte ( 102400 );?
          11 int ?len;?
          12 while ((len? = ?is.read(buf)) !=- 1 ) {?
          13 ????fos.write(buf, 0 ,len);?
          14 }
          ?
          15 fos.close();?
          16 is.close();?
          17
          18

          ?

          通過(guò)上面的代碼,我們完成了針對(duì)SQLServerBlob/Clob型字段操作.看起來(lái)非常簡(jiǎn)單,不過(guò),并非侮種數(shù)據(jù)庫(kù)都如此友善。讓我們接著來(lái)看看Oracle數(shù)據(jù)庫(kù)下的Blob/Clob字段讀寫,

          通過(guò)修改hibernate.cfg.xml中的Dialect配置完成數(shù)據(jù)庫(kù)切換后,我們?cè)俅芜\(yùn)行上面的TUser對(duì)象保存例程。

          程序運(yùn)行期間拋出異常:

          ?

          Hibernate:select hibernate_sequence.nextval from dual

          Hibernate:insert into T_USER (name, age,? image,resume. id) values(?, ?, ?, ?, ?)

          17:27:24,161 ERROR JDBCExceptionReporter:58 - - 不允許的操作: Streams type cannot be used in batching

          17:27:24,171 ERROR Sessionlmpl:2399 - Could not synchronize database state with session

          net.sf.hibernate.exception.GenericJDBCException:could not insert:[com.redsaga.hibernate.db.entity.TUser#6]

          ...

          ?

          觀察異常信息:streams type cannot be used in batching.這意味著Oracle JDBC不允許流操作以批量方式執(zhí)行(Oracle CLOB采用流機(jī)制作為數(shù)據(jù)讀寫方式)。

          這種錯(cuò)誤一般發(fā)生在hibernate.cfg.xml中的hibernate jdbc.batch_size設(shè)定大于0的情況,將hibernate.jdbc.batch_size修改為0即可消除。

          ?

          <hibernate-configuration>

          <session-factory>

          ???????????? ...

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

          ???????????? ...

          </session-factory>

          </hibernate-configuration>

          ?

          再次嘗試啟動(dòng)代碼,發(fā)現(xiàn)依然無(wú)法成功運(yùn)行,拋出異常如下:

          ?

          Hibernate: select hibernate_sequence.nextval from dual

          Hibernate: insert into T--USER? (name,? age,? image,resume,id) values(?,?,?,?,?)

          19:02:21,054 ERROR JDBCExceptionReporter:58 IO異常:Connection reset bypeer: socket write error

          19:02:21,054 ERROR JDBCExceptionReporter:58 I。異常:Connection reset by peer:socket write error

          19:02:21 。064 ERROR JDBCExceptionReporter:58Io異常:Connection reset by peer: socket wrto error

          19:02:21,064 ERROR Sessionlrnpl:2399 Could not synchronize database state with session

          net.sf.hibernate.exception.GenericJDSCException:? could not insert:[com.redsaga.hibernate.db.entity.TUser#27]

          ...

          ?

          為什么會(huì)出現(xiàn)這樣的情況?

          問(wèn)題出在OracceBlob/Clob字段獨(dú)特的訪問(wèn)方式,Oracle Blob/Clob字段本身?yè)碛幸粋€(gè)游標(biāo)(cursor) , JDBC必須通過(guò)游標(biāo)對(duì)Blob/ Clob字段進(jìn)行操作,在Blob/Clob字段被創(chuàng)建之前,我們無(wú)法獲取其游標(biāo)句柄,這也就意味著,我們必須首先創(chuàng)建一個(gè)空Blob/Clob字段,再?gòu)倪@個(gè)空Blob/Clob字段獲取游標(biāo),寫入我們所期望保存的數(shù)據(jù)。

          如果用JDBC代碼來(lái)表示這個(gè)過(guò)程,則得到如下代碼:

          ?

          //... 獲取JDBC連接

          dbconn.setAutoCommit(falee);

          // =======插入數(shù)據(jù),BLOB CLOB字段插入空值

          PreparedStatenent preStmt=

          dbconn.prepareStatement(

          ??? "insert into T_USER (name, age,? id,image,resume) values

          ???? (?,?,?,?,?)");

          preStmt.setString(1,"Shark");

          preStmt.setInt(2,20);

          preStmt.setInt(3,5);?

          // 通過(guò)oracle.sgl.BLOB/CLOB.empty_lob()方法構(gòu)造空Blob/Clob對(duì)象

          preStmt.setBlob(4 ,oracle.sgl.BLOB.empty_lob());

          preStmt.setClob(5,oracle.sgl.CLOB.empty_lob());

          preStmt.executeUpdate();

          preStmt.close():

          //========== 再次從庫(kù)表讀出,獲得Blob/Clob句柄

          preStmt=

          ? dbconn.prepareStatement(

          ????? "select? image,resume from T_USER where id=?for update');

          preStmt.setint(l,5);

          ResultSet rset=preStmt.executeQuery();

          // 注意我們這里需要引用Oracle原生BLOB定義,如果使用了Weblogic JDBC Vendor

          // 則應(yīng)使用weblogic.jdbc.vendor.oracle. OracleThinBLob/OracleThinCLOb

          rset.next();

          oracle.sql.BLOB imqBlob = (oracle.sql.BLOB) rset.getBlob(1);

          oracle.sql.CLOB resClob = (oracle.sql.CLOB) rset.getClob(2);

          //======= 將二進(jìn)創(chuàng)數(shù)據(jù)寫入Blob

          FileInputStream inStream = new FileinputStream("c\\inimage.jpg");

          OutputStream outStream = imgBlob.getBinaryOutputStream();

          byte[] buf=new byte[10240];//10K 讀取緩存

          int len;

          while((len=inStream.read(buf))>0){

          ?? outStream.write(buf,0,len);

          }

          inStream.close();

          outStream.close():

          //======= 將字符串寫入Clob

          resClob.putString(1 ,"This is my Glob"

          //======= Blob/Clob字段更新到數(shù)據(jù)序

          preStmt= dbconn.prepareStatement("update T_USER set? image=?,? resume=? where id=?");

          preStmt.setBlob(1,imgBlob);

          preStmt.setClob(2,resClob):

          preStmt.setlnt(3 ,5);

          preStmt.executeUpdate();

          preStmt.close():

          dbconn.commit();

          dbconn.close():

          ?

          上面的代碼說(shuō)明了OracleBlob/Clob字段操作的一般機(jī)制,那么,基于Hibernate的持久層實(shí)現(xiàn)中,應(yīng)該如何對(duì)Blob/Clob字段進(jìn)行處理?

          我們知道,Hibernate底層數(shù)據(jù)訪問(wèn)機(jī)制仍然是基于JDBC實(shí)現(xiàn),那么也就意味著我們必須在Hibernate中模擬JDBC的訪問(wèn)流程:

          TUser user=new TUser();

          user.setAge(new Integer(20));

          user.setName("Shark');

          user.setImage(Hibernate.createSlob(new byte [1])):

          user.setResume(Hibernate.createClob(" "));// 注意這里的參教是一個(gè)空格

          Transaction tx=session.beginTransaction();

          session.save(user):

          // 調(diào)用flush方法,強(qiáng)制Hibernate立即執(zhí)行insert sql

          session.flush();

          // 通過(guò)refresh方法,強(qiáng)制Hibernate執(zhí)行select for update

          session.refresh(user, LockMode.UPGRADE);

          // Blob寫入實(shí)際內(nèi)容

          oracle.sql.BLOB blob=(oracle.sql.BLOB)user.getImage();

          OutputStream out=blob. getBinaryOutputStream();

          FileInputStream imgis=new FileInputStream("C:\\inimage.jpg");

          byte[] buf=new byte[10240];//10K 緩存

          int len;

          while((len=imgis.read(buf))>0){

          ? out.write(buf,0,len);

          }

          imgis.close();

          out.close();

          // Clob寫入實(shí)際內(nèi)容

          oracle.sql.CLOB clob=(oracle.sgl.CLOB)? user.getResume();

          java.io.Writer writer = clob.getCharacterOutputStream();

          writer.write("this is my? resume');

          writer.close();

          session.save(user);

          tx.commit();

          實(shí)際應(yīng)用中,對(duì)于Clob字段,我們可以簡(jiǎn)單地將其映射為String類型,不過(guò)在這種情況下需要注意,Oracle Thin Driver對(duì)Clob字段支持尚有欠缺,當(dāng)Clob內(nèi)容超出4000字節(jié)時(shí)將無(wú)法讀取,而Oracle OCI Driver(需要在本地安裝Oracle客戶端組件)則可以成功地完成大容量Clob字段的操作。

          上面的代碼中,我們通過(guò)Session.save/flush/refresh方法的組合使用,實(shí)現(xiàn)了上面JDBC代碼中的Blob/Clob訪問(wèn)邏輯。

          Blob/Clob 字段的Hibernate保存實(shí)現(xiàn)如上所述,相對(duì)來(lái)講,讀取則沒(méi)有太多的障礙,之前的讀取代碼依然可以正常運(yùn)行。

          對(duì)于上面的實(shí)現(xiàn),相信大家都感覺(jué)到了一些Bad Smell,如果Blob/Clob字段普遍存在,那么我們的持久層邏輯中可能遍布如此復(fù)雜的數(shù)據(jù)存儲(chǔ)邏輯、并與數(shù)據(jù)庫(kù)原生類緊密禍

          如何解決這些問(wèn)題?

          回憶之前關(guān)于自定義數(shù)據(jù)類型的討論。通過(guò)自定義數(shù)據(jù)類型我們可以對(duì)數(shù)據(jù)的通用特征進(jìn)行抽象,那么,對(duì)于OracleBlob/Clob字段,我們是否可以也對(duì)其進(jìn)行抽象,并以其作為所有Oracle Blob/Clob字段的映射類型?

          下面的StringClobType實(shí)現(xiàn)了這一目標(biāo):

          public class StringClobType implements UserType{

          ??? private static final String ORACLE_DRIVER_NAME="Oracle JDBC driver";

          ?????? private static final int ORACLE_DRIVER_MAJOR_VERSION=9;

          ??? private static final int ORACLE_DRIVER_MINOR_VERSION=0;

          ??? public int[] sqlTypes(){

          ?????? return new int[] {Types.CLOB};

          ??? }

          ??? public Class returnedClass{

          ?????? return String.class;

          ??? }

          ??? public boolean equals(Object x, object y){

          ?????? return org.apache.commons.lang.ObjectUtils.equals(x, y);

          ??? }

          ??? public Object nullSafeGet(ResultSet rs, String[] names, Object owner)

          ??? throws HibernateException,SQLException{

          ?????? Clob clob=rs.getClob(names(O]);

          ?????? return(clob==null ? null:clob.getSubString(l,? (int) clob.length())):

          ??? }

          ??? public void nullSafeSet(PreparedStatement st Object value, int index)?

          ??? throws HibernateException, SQLException{

          ?????? DatabaseMetaData dbMetaData=st.getConnection().getMetaData();

          ?????? if (value==null)

          ?????????? st.setNull(index,? sqiTypes()(0));

          ?????? else

          //???????? 本實(shí)現(xiàn)僅僅適用于Oracle數(shù)據(jù)序9.0以上版本

          ?????? ??? if

          ?????????? (ORACLE_DRIVER_NAME.equals(dbMetaData.getDriverName( ,))(

          ????????????? if((dbMetaData.getDriverMajorVersion()?

          ????????????????? ???>=ORACLE-DRIVER-MAJOR-VERSION)

          ????????????????? &&(dbMetaData.getDriverMinorVersion()??

          ???????????????????? >=ORACLE-DRIVER-MINOR-VERSION))

          ???????????????????????? try

          //??????????????? 通過(guò)動(dòng)態(tài)加載方式進(jìn)免編譯期對(duì)Oracle JDBC的依賴

          ???????????????????????? Class oracleClobClass=Class.forName('oracle.sgl.CLOB");

          //????????????????????????????? 動(dòng)態(tài)調(diào)用createTemporary方法

          ??????????????????????????????? Class partypes[]=new Class[3];

          ??????????????????????????????? partypes[0]=Connection.class;

          ??????????????????????????????? partypes[1]=Boolean.TYPE;

          ??????????????????????????????? partypes(2]=Integer.TYPE;

          ??????????????????????????????? Method createTemporaryMethod=

          ?????????????????????????????????? oracleClobClass.getDeclaredMethod(

          ????????????????????????????????????????? "createTemporaxy “,

          ????????????????????????????????????????? partypes);

          ??????????????????????????????? Field durationSessionField=

          ?????????????????????????????????? oracleClobClass.getField("DURATION-SESSION");

          ??????????????????????????????? Object arglist[]=new 0bject[3]:

          ?????????????????????????????????? Connection conn=

          ?????????????????????????????????????? st.getConnection().getMetaData().getConnection();

          //????????????????????????????? 數(shù)據(jù)庫(kù)連接類型必須為OracleConnection

          //????????????????????????????? 萊些應(yīng)用服務(wù)器會(huì)使用自帶Oracle JDBC Wrapper,如Weblogic

          //????????????????????????? ??? 這里需要特別注意

          ??????????????????????????????? Class oracleConnectionClass=

          ?????????????????????????????????? Class.forName("oracle.jdbc.OracleConnection");

          ??????????????????????????????? if(!oracleConnectionClass

          ?????????????????????????????????????? .isAssignableFrom(conn.getClass())){

          ?????????????????????????????????? throw new HibernateException(

          ????????????????????????????????????????? "Must be a oracle.jdbc.OracleConnection:.

          ????????????????????????????????????????? +conn.getClass().getName());

          ??????????????????????????????? }

          ??????????????????????????????? arglist[0] = conn;

          ??????????????????????????????? arglist(1] = Boolean.TRUE;

          ??????????????????????????????? arolist[2] = durationSessionField.get(null);

          ??????????????????????????????? Object tempClob =createTemporaryMethod.invoke(null,arglist);

          ??????????????????????????????? partypes=new Class[l];

          ??????????????????????????????? partypes[0]=Integer.TYPE;

          ??????????????????????????????? Method openMethod =oracleClobClass.getDeclaredMethod("open",partypes);

          ??????????????????????????????? Field modeReadWriteField =oracleClobClass.getField("MODE_READWRITE");

          ??????????????????????????????? arglist = new Object(l];

          ??????????????????????????????? arglis[0] = modeReadWriteField.get(null);

          ??????????????????????????????? openMethod.invoke(tempClob, arglist);

          ??????????????????????????????? Method getCharacterOutputStreamMethod=oracleClobClass.getDeclaredMethod("getCharacterOutputStream',null) ;

          //???????????????????????????????????? call the getCharacterOutpitStream method

          ?????????????????????????????????????? Writer tempClobWriter =(Writer)getCharacterOutputStreamMethod.invoke(tempClob,null);

          //???????????????????????????????????? 將參數(shù)寫入Clob

          ?????????????????????????????????????? tempClobwriter.write((String) value);

          ?????????????????????????????????????? tempClobWriter.flush();

          ?????????????????????????????????????? tempClobWriter.Close();

          ??????????????????????????????????????

          //???????????????????????????????????? close? clob

          ?????????????????????????????????????? Method closeMethod=oracleClobClass.getDeclaredMethod("close", null);

          ?????????????????????????????????????? closeMethod.invoke(tempClob, null);

          ?????????????????????????????????????? st.setClob(index,? (Clob) tempClob);

          ??????????????????????????????? )catch? (ClassNotFoundException e){

          ?????????????????????????????????? throw new HibernateException("Unable to find a required class.\n"+e.getMessage()):

          ??????????????????????????????? }catch (NOSuchMethodException e){

          ?????????????????????????????????? throw new HibernateException("Unable to find a required method.\n"+e.getMessage()):

          ??????????????????????????????? }catch (NoSuchFieldException e){

          ?????????????????????????????????? throw new HibernateException("Unable to find a required field.\n"+e.getMessage());

          ??????????????????????????????? }catch (IllegalAccessException e){

          ?????????????????????????????????? throw new HibernateException("Unable to access a required method or field.\n"+e.getMessage());

          ?????????????????????????????????? catch (InvocationTargetException e){

          ?????????????????????????????????????? throw new HibernateException(e.getMessage());

          ?????????????????????????????????????? {? catch (IOException e){

          ????????????????????????????????????????? throw new HibernateException(e.getMessage());

          ?????????????????????????????????????? }

          ?????????????????????????????????????? else

          ?????????????????????????????????????? throw new HibernateException(

          ????????????????????????????????????????????? "No CLOBS support.Use driver version"

          ????????????????????????????????????????????? +ORACLE_DRIVER_MAJOR_VERSION

          ????????????????????????????????????????????? +" minor"

          ????????????????????????????????????????????? +ORACLE_DRIVER_MINOR_VERSION);

          ?????????????????????????????????????? }

          ?????????????????????????????????? }else

          ?????????????????????????????????? String str = (String)? value;

          ??? ??????????????????????????????? StrinaReader r = new StringReader(str);

          ?????????????????????????????????? St.setCharacterStream(index, r, str.length());

          ??????????????????????????????? }

          ??? }

          ??? public Object deepCopy(Object value){

          ?????? if(value==null)

          ?????????? return null;

          ?????? return new String((String)? value);

          ??? }

          ??? public boolean isMutable(){

          ?????? return false

          ??? }

          }

          上面這段代碼,重點(diǎn)在于nullSafeSet方法的實(shí)現(xiàn),nullSafeSet中通過(guò)Java Reflection機(jī)制,解除了編譯期的Oralce JDBC原生類依賴。同時(shí),借助Oracle JDBC提供的原生功能完成了Clob字段的寫入,Clob字段的寫入操作由于涉及特定數(shù)據(jù)庫(kù)內(nèi)部實(shí)現(xiàn)細(xì)節(jié),這里就不多費(fèi)唇舌,大家可參見Oracle JDBC Java Doc.

          這段代碼是由筆者根據(jù)Ali Ibrahim, Scott Miller的代碼修改而來(lái)的(原版請(qǐng)參見httpJ/www.hibemate, org /56.html ),支持Oracle 9以上版本,Oracle 8對(duì)應(yīng)的實(shí)現(xiàn)請(qǐng)參見上述網(wǎng)址。

          同樣的道理,讀者可以根據(jù)以上例程,編寫自己的ByteBlobType以實(shí)現(xiàn)byte[]Blob的映射。

          另外,此代碼必須運(yùn)行在最新版的Oracle JDBC Driver(筆者所用版本為Oracle9i9.2.0.5 for JDK1.4,如果使用9.2.0.3或之前版本則在新建l更卻刪除數(shù)據(jù)時(shí)可能會(huì)遇到“nomore data read from socket”錯(cuò)誤)。

          ?


          評(píng)論

          # re: Hibernate實(shí)現(xiàn)Clob和Blob對(duì)象的存取  回復(fù)  更多評(píng)論   

          2007-10-30 14:17 by libinglin
          很好,我正不知道用Hibernate如何存orcle的大對(duì)象呢,看了下,明白了

          # re: Hibernate實(shí)現(xiàn)Clob和Blob對(duì)象的存取  回復(fù)  更多評(píng)論   

          2008-05-27 15:52 by roc
          兄臺(tái),保持文章更新啊。
          這篇帖子很有用處~~謝謝。

          # re: Hibernate實(shí)現(xiàn)Clob和Blob對(duì)象的存取  回復(fù)  更多評(píng)論   

          2008-06-15 15:37 by 程波
          Caused by: java.sql.SQLException: 不允許的操作: streams type cannot be used in batching
          我用的ORALCE 報(bào)這個(gè)異常是怎么回事呢 ?

          # re: Hibernate實(shí)現(xiàn)Clob和Blob對(duì)象的存取  回復(fù)  更多評(píng)論   

          2008-06-15 16:15 by 程波
          org.hibernate.lob.SerializableBlob cannot be cast to oracle.sql.BLOB
          怎么回事啊 樓主幫幫我啊

          # re: Hibernate實(shí)現(xiàn)Clob和Blob對(duì)象的存取  回復(fù)  更多評(píng)論   

          2008-09-20 20:31 by Yvon
          謝謝博主的分享

          # re: Hibernate實(shí)現(xiàn)Clob和Blob對(duì)象的存取  回復(fù)  更多評(píng)論   

          2010-01-12 15:51 by 落Nicety
          8錯(cuò)

          # re: Hibernate實(shí)現(xiàn)Clob和Blob對(duì)象的存取  回復(fù)  更多評(píng)論   

          2011-09-27 12:44 by K仔
          很好很詳細(xì)!

          # re: Hibernate實(shí)現(xiàn)Clob和Blob對(duì)象的存取  回復(fù)  更多評(píng)論   

          2012-10-07 03:11 by 電風(fēng)扇的
          非常感謝

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 龙里县| 宜城市| 石首市| 达日县| 怀化市| 正安县| 桦南县| 讷河市| 托克托县| 大城县| 苗栗市| 张家界市| 汉寿县| 高阳县| 剑川县| 武宁县| 轮台县| 长丰县| 贵阳市| 鄂尔多斯市| 蓬溪县| 天津市| 蓬莱市| 文山县| 湟源县| 镇赉县| 郯城县| 秭归县| 乐业县| 威信县| 同德县| 个旧市| 宜良县| 横峰县| 肥西县| 特克斯县| 通辽市| 平利县| 汝州市| 高淳县| 苗栗市|