<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 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

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

    ?

    ?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創(chuàng)建了對(duì)應(yīng)的BlobClob對(duì)象。其中Blob對(duì)象基于一個(gè)FileInputStream構(gòu)建,而Clob對(duì)象基于一個(gè)字符串構(gòu)建。

    ?

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

    ?

    ?1 // ?假設(shè)庫表記錄的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字段內(nèi)容?
    ?5 System.out.println( " User?resume=> " + resume.getSubString( 1 ,( int )resume.length()));?
    ?6 Blob?img? = ?user.getImage();?
    ?7 // ?通過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

    ?

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

    通過修改hibernate.cfg.xml中的Dialect配置完成數(shù)據(jù)庫切換后,我們再次運(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)依然無法成功運(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)這樣的情況?

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

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

    ?

    //... 獲取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);?

    // 通過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():

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

    ?

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

    我們知道,Hibernate底層數(shù)據(jù)訪問機(jī)制仍然是基于JDBC實(shí)現(xiàn),那么也就意味著我們必須在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(" "));// 注意這里的參教是一個(gè)空格

    Transaction tx=session.beginTransaction();

    session.save(user):

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

    session.flush();

    // 通過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字段,我們可以簡單地將其映射為String類型,不過在這種情況下需要注意,Oracle Thin Driver對(duì)Clob字段支持尚有欠缺,當(dāng)Clob內(nèi)容超出4000字節(jié)時(shí)將無法讀取,而Oracle OCI Driver(需要在本地安裝Oracle客戶端組件)則可以成功地完成大容量Clob字段的操作。

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

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

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

    如何解決這些問題?

    回憶之前關(guān)于自定義數(shù)據(jù)類型的討論。通過自定義數(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

    //??????????????? 通過動(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ù)庫連接類型必須為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中通過Java Reflection機(jī)制,解除了編譯期的Oralce JDBC原生類依賴。同時(shí),借助Oracle JDBC提供的原生功能完成了Clob字段的寫入,Clob字段的寫入操作由于涉及特定數(shù)據(jù)庫內(nèi)部實(shí)現(xiàn)細(xì)節(jié),這里就不多費(fèi)唇舌,大家可參見Oracle JDBC Java Doc.

    這段代碼是由筆者根據(jù)Ali Ibrahim, Scott Miller的代碼修改而來的(原版請(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)扇的
    非常感謝

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


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 亚洲成人网在线观看| 国产成人免费午夜在线观看| 亚洲熟妇无码AV不卡在线播放| 亚洲AV无码久久精品色欲| 亚洲国产91精品无码专区| 67194成是人免费无码| 99爱在线精品视频免费观看9| 久久免费国产精品| 精品亚洲国产成人av| 亚洲精品无码永久在线观看男男| 亚洲最大视频网站| 1区1区3区4区产品亚洲 | 国产综合成人亚洲区| 亚洲91精品麻豆国产系列在线| 亚洲精品天天影视综合网| 亚洲欧洲日产国码无码久久99| yy6080亚洲一级理论| 国产又长又粗又爽免费视频| 在线观看成人免费| 成人免费毛片观看| 女人18毛片特级一级免费视频| 男女免费观看在线爽爽爽视频| 99国产精品视频免费观看| a毛片免费播放全部完整| 一区二区三区免费精品视频| 日本精品久久久久久久久免费| 国产偷国产偷亚洲高清人| 国产精品亚洲一区二区三区久久| 老司机亚洲精品影院在线观看| 国产精品亚洲а∨天堂2021 | 一个人免费高清在线观看| 曰批视频免费30分钟成人| 亚洲高清中文字幕免费| 57PAO成人国产永久免费视频| 亚洲免费电影网站| aa级一级天堂片免费观看| 成人片黄网站色大片免费| 国产精品公开免费视频| 高清在线亚洲精品国产二区| 亚洲精品视频在线看| 亚洲精品无码Av人在线观看国产 |