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

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

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

    Hibernate延遲加載機制(面試時常會問到的問題)

    延遲加載:
      延遲加載機制是為了避免一些無謂的性能開銷而提出來的,所謂延遲加載就是當在真正需要數據的時候,才真正執行數據加載操作。在Hibernate中提供了對實體對象的延遲加載以及對集合的延遲加載,另外在Hibernate3中還提供了對屬性的延遲加載。下面我們就分別介紹這些種類的延遲加載的細節。
    A、實體對象的延遲加載:

     如果想對實體對象使用延遲加載,必須要在實體的映射配置文件中進行相應的配置,如下所示:
    <hibernate-mapping>
     <class name=”com.neusoft.entity.User” table=”user” lazy=”true”>
       ……
     </class>
    </hibernate-mapping>
    通過將class的lazy屬性設置為true,來開啟實體的延遲加載特性。如果我們運行下面的代碼:
    User user=(User)session.load(User.class,”1”);(1)
    System.out.println(user.getName());(2)
    當運行到(1)處時,Hibernate并沒有發起對數據的查詢,如果我們此時通過一些調試工具(比如JBuilder2005的Debug工具),觀察此時user對象的內存快照,我們會驚奇的發現,此時返回的可能是User$EnhancerByCGLIB$$bede8986類型的對象,而且其屬性為null,這是怎么回事?還記得前面我曾講過session.load()方法,會返回實體對象的代理類對象,這里所返回的對象類型就是User對象的代理類對象。在Hibernate中通過使用CGLIB,來實現動態構造一個目標對象的代理類對象,并且在代理類對象中包含目標對象的所有屬性和方法,而且所有屬性均被賦值為null。通過調試器顯示的內存快照,我們可以看出此時真正的User對象,是包含在代理對象的CGLIB$CALBACK_0.target屬性中,當代碼運行到(2)處時,此時調用user.getName()方法,這時通過CGLIB賦予的回調機制,實際上調用CGLIB$CALBACK_0.getName()方法,當調用該方法時,Hibernate會首先檢查CGLIB$CALBACK_0.target屬性是否為null,如果不為空,則調用目標對象的 getName方法,如果為空,則會發起數據庫查詢,生成類似這樣的SQL語句:select * from user where id=’1’;來查詢數據,并構造目標對象,并且將它賦值到CGLIB$CALBACK_0.target屬性中。
       這樣,通過一個中間代理對象,Hibernate實現了實體的延遲加載,只有當用戶真正發起獲得實體對象屬性的動作時,才真正會發起數據庫查詢操作。所以實體的延遲加載是用通過中間代理類完成的,所以只有session.load()方法才會利用實體延遲加載,因為只有session.load()方法才會返回實體類的代理類對象。

    B、集合類型的延遲加載


    在 Hibernate的延遲加載機制中,針對集合類型的應用,意義是最為重大的,因為這有可能使性能得到大幅度的提高,為此Hibernate進行了大量的努力,其中包括對JDK Collection的獨立實現,我們在一對多關聯中,定義的用來容納關聯對象的Set集合,并不是java.util.Set類型或其子類型,而是 net.sf.hibernate.collection.Set類型,通過使用自定義集合類的實現,Hibernate實現了集合類型的延遲加載。為了對集合類型使用延遲加載,我們必須如下配置我們的實體類的關于關聯的部分:
     <hibernate-mapping>
       <class name=”com.neusoft.entity.User” table=”user”>
    …..
    <set name=”addresses” table=”address” lazy=”true” inverse=”true”>
    <key column=”user_id”/>
     <one-to-many class=”com.neusoft.entity.Arrderss”/>
    </set>
       </class>
     </hibernate-mapping>
    通過將<set>元素的lazy屬性設置為true來開啟集合類型的延遲加載特性。我們看下面的代碼:
    User user=(User)session.load(User.class,”1”);
    Collection addset=user.getAddresses();      (1)
    Iterator it=addset.iterator();               (2)
    while(it.hasNext()){
     Address address=(Address)it.next();
     System.out.println(address.getAddress());
    }
    當程序執行到(1)處時,這時并不會發起對關聯數據的查詢來加載關聯數據,只有運行到(2)處時,真正的數據讀取操作才會開始,這時Hibernate會根據緩存中符合條件的數據索引,來查找符合條件的實體對象。
    這里我們引入了一個全新的概念——數據索引,下面我們首先將接一下什么是數據索引。在 Hibernate中對集合類型進行緩存時,是分兩部分進行緩存的,首先緩存集合中所有實體的id列表,然后緩存實體對象,這些實體對象的id列表,就是所謂的數據索引。當查找數據索引時,如果沒有找到對應的數據索引,這時就會一條select SQL的執行,獲得符合條件的數據,并構造實體對象集合和數據索引,然后返回實體對象的集合,并且將實體對象和數據索引納入Hibernate的緩存之中。另一方面,如果找到對應的數據索引,則從數據索引中取出 id列表,然后根據id在緩存中查找對應的實體,如果找到就從緩存中返回,如果沒有找到,在發起select SQL查詢。在這里我們看出了另外一個問題,這個問題可能會對性能產生影響,這就是集合類型的緩存策略。如果我們如下配置集合類型:
    <hibernate-mapping>
       <class name=”com.neusoft.entity.User” table=”user”>
    …..
    <set name=”addresses” table=”address” lazy=”true” inverse=”true”>
     <cache usage=”read-only”/>
    <key column=”user_id”/>
     <one-to-many class=”com.neusoft.entity.Arrderss”/>
    </set>
       </class>
     </hibernate-mapping>
    這里我們應用了<cache usage=”read-only”/>配置,如果采用這種策略來配置集合類型,Hibernate將只會對數據索引進行緩存,而不會對集合中的實體對象進行緩存。如上配置我們運行下面的代碼:
    User user=(User)session.load(User.class,”1”);
    Collection addset=user.getAddresses();    
    Iterator it=addset.iterator();             
    while(it.hasNext()){
     Address address=(Address)it.next();
     System.out.println(address.getAddress());
    }
    System.out.println(“Second query……”);
    User user2=(User)session.load(User.class,”1”);
    Collection it2=user2.getAddresses();
    while(it2.hasNext()){
     Address address2=(Address)it2.next();
     System.out.println(address2.getAddress());
    }
    運行這段代碼,會得到類似下面的輸出:
    Select * from user where id=’1’;
    Select * from address where user_id=’1’;
    Tianjin
    Dalian
    Second query……
    Select * from address where id=’1’;
    Select * from address where id=’2’;
    Tianjin
    Dalian
    我們看到,當第二次執行查詢時,執行了兩條對address表的查詢操作,為什么會這樣?這是因為當第一次加載實體后,根據集合類型緩存策略的配置,只對集合數據索引進行了緩存,而并沒有對集合中的實體對象進行緩存,所以在第二次再次加載實體時,Hibernate找到了對應實體的數據索引,但是根據數據索引,卻無法在緩存中找到對應的實體,所以Hibernate根據找到的數據索引發起了兩條select SQL的查詢操作,這里造成了對性能的浪費,怎樣才能避免這種情況呢?我們必須對集合類型中的實體也指定緩存策略,所以我們要如下對集合類型進行配置:
    <hibernate-mapping>
       <class name=”com.neusoft.entity.User” table=”user”>
    …..
    <set name=”addresses” table=”address” lazy=”true” inverse=”true”>
     <cache usage=”read-write”/>
    <key column=”user_id”/>
     <one-to-many class=”com.neusoft.entity.Arrderss”/>
    </set>
       </class>
     </hibernate-mapping>
    此時Hibernate會對集合類型中的實體也進行緩存,如果根據這個配置再次運行上面的代碼,將會得到類似如下的輸出:
    Select * from user where id=’1’;
    Select * from address where user_id=’1’;
    Tianjin
    Dalian
    Second query……
    Tianjin
    Dalian
    這時將不會再有根據數據索引進行查詢的SQL語句,因為此時可以直接從緩存中獲得集合類型中存放的實體對象。

    C、屬性延遲加載:

      在Hibernate3中,引入了一種新的特性——屬性的延遲加載,這個機制又為獲取高性能查詢提供了有力的工具。在前面我們講大數據對象讀取時,在User對象中有一個resume字段,該字段是一個java.sql.Clob類型,包含了用戶的簡歷信息,當我們加載該對象時,我們不得不每一次都要加載這個字段,而不論我們是否真的需要它,而且這種大數據對象的讀取本身會帶來很大的性能開銷。在Hibernate2中,我們只有通過我們前面講過的面性能的粒度細分,來分解User類,來解決這個問題(請參照那一節的論述),但是在Hibernate3中,我們可以通過屬性延遲加載機制,來使我們獲得只有當我們真正需要操作這個字段時,才去讀取這個字段數據的能力,為此我們必須如下配置我們的實體類:
    <hibernate-mapping>
     <class name=”com.neusoft.entity.User” table=”user”>
    ……
    <property name=”resume” type=”java.sql.Clob” column=”resume” lazy=”true”/>
       </class>
    </hibernate-mapping>
    通過對<property>元素的lazy屬性設置true來開啟屬性的延遲加載,在Hibernate3中為了實現屬性的延遲加載,使用了類增強器來對實體類的Class文件進行強化處理,通過增強器的增強,將CGLIB的回調機制邏輯,加入實體類,這里我們可以看出屬性的延遲加載,還是通過CGLIB來實現的。CGLIB是 Apache的一個開源工程,這個類庫可以操縱java類的字節碼,根據字節碼來動態構造符合要求的類對象。根據上面的配置我們運行下面的代碼:
    String sql=”from User user where user.name=’zx’ ”;
    Query query=session.createQuery(sql);   (1)
    List list=query.list();
    for(int i=0;i<list.size();i++){
     User user=(User)list.get(i);
     System.out.println(user.getName());
     System.out.println(user.getResume());   (2)
    }
    當執行到(1)處時,會生成類似如下的SQL語句:
    Select id,age,name from user where name=’zx’;
    這時Hibernate會檢索User實體中所有非延遲加載屬性對應的字段數據,當執行到(2)處時,會生成類似如下的SQL語句:
    Select resume from user where id=’1’;
    這時會發起對resume字段數據真正的讀取操作。


    posted on 2009-11-24 11:14 MichaelLee 閱讀(866) 評論(0)  編輯  收藏 所屬分類: Hibernater

    <2009年11月>
    25262728293031
    1234567
    891011121314
    15161718192021
    22232425262728
    293012345

    導航

    統計

    公告

    ====Michael Lee====
    =Java Sofware Engineer=
    =Work @ Beijing=
    ---再煩,也別忘微笑;再急,也要注意語氣;再苦,也別忘堅持;再累,也要愛自己!---
    ---低調做人,你會一次比一次穩健;高調做事,你會一次比一次優秀---
    ---成功的時候不要忘記過去;失敗的時候不要忘記還有未來---

    常用鏈接

    留言簿(2)

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲免费人成在线视频观看 | 精品一区二区三区免费毛片爱 | 黄色网址免费观看| 婷婷精品国产亚洲AV麻豆不片| 国产免费一级高清淫曰本片| 国产精品亚洲αv天堂无码| 国产精品视频全国免费观看| 国产成人精品日本亚洲专区61| 成人无码精品1区2区3区免费看 | 性感美女视频免费网站午夜 | eeuss影院www天堂免费| 国产亚洲大尺度无码无码专线| 两个人看的www高清免费视频| 亚洲午夜福利在线观看| 日本免费一区二区三区四区五六区 | 青青视频观看免费99| 亚洲色少妇熟女11p| 日韩精品亚洲专区在线观看| 九九99热免费最新版| 色拍自拍亚洲综合图区| **实干一级毛片aa免费| 亚洲熟妇AV日韩熟妇在线| 亚洲AⅤ永久无码精品AA | 色五月五月丁香亚洲综合网| 亚洲精品动漫人成3d在线| 国内精品免费视频精选在线观看 | 亚洲国产成人久久精品app| 青青青国产免费一夜七次郎| 一个人晚上在线观看的免费视频| 亚洲国产精品无码久久SM| 久久精品网站免费观看| 成人a毛片免费视频观看| 亚洲国产成人久久综合碰碰动漫3d| 和日本免费不卡在线v| 美女被免费视频网站a| 亚洲AV美女一区二区三区| 成全影视免费观看大全二| 久99久无码精品视频免费播放| 亚洲乱码一二三四五六区| 亚洲国产精品无码久久青草| 久久国产高潮流白浆免费观看|