<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 6, comments - 8, trackbacks - 0, articles - 6
      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

    Hibernate實現Clob和Blob對象的存取

    Posted on 2006-12-07 10:27 sxt 閱讀(12449) 評論(8)  編輯  收藏

    作為測試,我們新建一個TUser對象,其image屬性中,保存了一個圖片文件的二進制內容。而其resume屬性,我們以一個簡單的字符串作為填充。

    ?

    ?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

    上面的代碼中,我們通過Hibemate.createBlobHibemate.createClob創建了對應的BlobClob對象。其中Blob對象基于一個FileInputStream構建,而Clob對象基于一個字符串構建。

    ?

    完成了寫入操作,對應的讀取操作代碼如下:

    ?

    ?1 // ?假設庫表記錄的id字段等于3?
    ?2 TUser?user = (TUser)??session.load(TUger.elaa.,?load(TUser. class ,?? new ?Integer( 3 ));?
    ?3 Clob?resume = user.getResume();?
    ?4 // ?通過Clob.getSubString()方法獲取Clob字段內容?
    ?5 System.out.println( " User?resume=> " + resume.getSubString( 1 ,( int )resume.length()));?
    ?6 Blob?img? = ?user.getImage();?
    ?7 // ?通過Blob.getBinaryS=ream()方法獲取二進制流?
    ?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

    ?

    通過上面的代碼,我們完成了針對SQLServerBlob/Clob型字段操作.看起來非常簡單,不過,并非侮種數據庫都如此友善。讓我們接著來看看Oracle數據庫下的Blob/Clob字段讀寫,

    通過修改hibernate.cfg.xml中的Dialect配置完成數據庫切換后,我們再次運行上面的TUser對象保存例程。

    程序運行期間拋出異常:

    ?

    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不允許流操作以批量方式執行(Oracle CLOB采用流機制作為數據讀寫方式)

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

    ?

    <hibernate-configuration>

    <session-factory>

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

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

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

    </session-factory>

    </hibernate-configuration>

    ?

    再次嘗試啟動代碼,發現依然無法成功運行,拋出異常如下:

    ?

    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]

    ...

    ?

    為什么會出現這樣的情況?

    問題出在OracceBlob/Clob字段獨特的訪問方式,Oracle Blob/Clob字段本身擁有一個游標(cursor) , JDBC必須通過游標對Blob/ Clob字段進行操作,在Blob/Clob字段被創建之前,我們無法獲取其游標句柄,這也就意味著,我們必須首先創建一個空Blob/Clob字段,再從這個空Blob/Clob字段獲取游標,寫入我們所期望保存的數據。

    如果用JDBC代碼來表示這個過程,則得到如下代碼:

    ?

    //... 獲取JDBC連接

    dbconn.setAutoCommit(falee);

    // =======插入數據,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);?

    // 通過oracle.sgl.BLOB/CLOB.empty_lob()方法構造空Blob/Clob對象

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

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

    preStmt.executeUpdate();

    preStmt.close():

    //========== 再次從庫表讀出,獲得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

    // 則應使用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);

    //======= 將二進創數據寫入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字段更新到數據序

    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():

    ?

    上面的代碼說明了OracleBlob/Clob字段操作的一般機制,那么,基于Hibernate的持久層實現中,應該如何對Blob/Clob字段進行處理?

    我們知道,Hibernate底層數據訪問機制仍然是基于JDBC實現,那么也就意味著我們必須在Hibernate中模擬JDBC的訪問流程:

    TUser user=new TUser();

    user.setAge(new Integer(20));

    user.setName("Shark');

    user.setImage(Hibernate.createSlob(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.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寫入實際內容

    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();

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

    上面的代碼中,我們通過Session.save/flush/refresh方法的組合使用,實現了上面JDBC代碼中的Blob/Clob訪問邏輯。

    Blob/Clob 字段的Hibernate保存實現如上所述,相對來講,讀取則沒有太多的障礙,之前的讀取代碼依然可以正常運行。

    對于上面的實現,相信大家都感覺到了一些Bad Smell,如果Blob/Clob字段普遍存在,那么我們的持久層邏輯中可能遍布如此復雜的數據存儲邏輯、并與數據庫原生類緊密禍

    如何解決這些問題?

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

    下面的StringClobType實現了這一目標:

    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

    //???????? 本實現僅僅適用于Oracle數據序9.0以上版本

    ?????? ??? if

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

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

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

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

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

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

    //??????????????? 通過動態加載方式進免編譯期對Oracle JDBC的依賴

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

    //????????????????????????????? 動態調用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();

    //????????????????????????????? 數據庫連接類型必須為OracleConnection

    //????????????????????????????? 萊些應用服務器會使用自帶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);

    //???????????????????????????????????? 將參數寫入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

    ??? }

    }

    上面這段代碼,重點在于nullSafeSet方法的實現,nullSafeSet中通過Java Reflection機制,解除了編譯期的Oralce JDBC原生類依賴。同時,借助Oracle JDBC提供的原生功能完成了Clob字段的寫入,Clob字段的寫入操作由于涉及特定數據庫內部實現細節,這里就不多費唇舌,大家可參見Oracle JDBC Java Doc.

    這段代碼是由筆者根據Ali Ibrahim, Scott Miller的代碼修改而來的(原版請參見httpJ/www.hibemate org /56.html ),支持Oracle 9以上版本,Oracle 8對應的實現請參見上述網址。

    同樣的道理,讀者可以根據以上例程,編寫自己的ByteBlobType以實現byte[]Blob的映射。

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

    ?


    評論

    # re: Hibernate實現Clob和Blob對象的存取  回復  更多評論   

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

    # re: Hibernate實現Clob和Blob對象的存取  回復  更多評論   

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

    # re: Hibernate實現Clob和Blob對象的存取  回復  更多評論   

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

    # re: Hibernate實現Clob和Blob對象的存取  回復  更多評論   

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

    # re: Hibernate實現Clob和Blob對象的存取  回復  更多評論   

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

    # re: Hibernate實現Clob和Blob對象的存取  回復  更多評論   

    2010-01-12 15:51 by 落Nicety
    8錯

    # re: Hibernate實現Clob和Blob對象的存取  回復  更多評論   

    2011-09-27 12:44 by K仔
    很好很詳細!

    # re: Hibernate實現Clob和Blob對象的存取  回復  更多評論   

    2012-10-07 03:11 by 電風扇的
    非常感謝

    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    主站蜘蛛池模板: 香蕉免费在线视频| 久久九九亚洲精品| 色影音免费色资源| aa级女人大片喷水视频免费| 亚洲精品第一国产综合野| 亚洲AV综合色区无码一区爱AV | 亚洲尤码不卡AV麻豆| 手机在线毛片免费播放| 18女人腿打开无遮掩免费| 99久久婷婷免费国产综合精品| 久久人午夜亚洲精品无码区| 亚洲一区欧洲一区| 色婷婷亚洲十月十月色天| 亚洲成AV人片在线观看无| 亚洲综合国产精品第一页| yy6080亚洲一级理论| 好爽…又高潮了免费毛片| 18勿入网站免费永久| 24小时日本韩国高清免费| 久久久久国产免费| 久久国产乱子精品免费女 | 亚洲一级片内射网站在线观看| 国产又粗又猛又爽又黄的免费视频| 一本岛高清v不卡免费一三区| 一级毛片在线免费看| 毛片免费在线观看| 精品国产麻豆免费人成网站| fc2成年免费共享视频网站| 美女被免费视频网站| 国产精品国产亚洲区艳妇糸列短篇| 亚洲中文字幕无码久久| 亚洲中文精品久久久久久不卡| 亚洲综合一区二区精品久久| 亚洲色大成网站www永久| 亚洲美女视频网址| 亚洲国产精品成人久久久 | 免费可以在线看A∨网站| 皇色在线视频免费网站| 久久久久久国产精品免费免费 | 亚洲色大成网站www永久男同| 亚洲日韩一中文字暮|