1. 程式人生 > >什麼是hibernate的延遲載入,什麼時候使用延時載入,為什麼要用延時載入?

什麼是hibernate的延遲載入,什麼時候使用延時載入,為什麼要用延時載入?

所謂懶載入(lazy)就是延時載入,延遲載入。

什麼時候用懶載入呢,我只能回答要用懶載入的時候就用懶載入。

至於為什麼要用懶載入呢,就是當我們要訪問的資料量過大時,明顯用快取不太合適,

因為記憶體容量有限 ,為了減少併發量,減少系統資源的消耗,

我們讓資料在需要的時候才進行載入,這時我們就用到了懶載入。

比如部門ENTITY和員工ENTITY,部門與員工1對多,如果lazy設定為false,那麼只要載入了一個部門的po,就會根據一對多配置的關係把所有員工的po也加載出來。但是實際上有時候只是需要用到部門的資訊,不需要用到員工的資訊,這時員工po的載入就等於浪費資源。如果lazy設定為true,那麼只有當你訪問部門po的員工資訊時候才回去載入員工的po的資訊。

hibernate3.0中lazy有三個值,true,false,proxy,預設的是lazy="proxy".
具體設定成什麼要看你的需求,並不是說哪個設定就是最好的。
假如在student物件中包含一個head物件
如果你確定在用student物件的時候就要用到head物件裡的屬性,那你就設定立即載入,因為設定立即載入那麼在查詢student的同時就會查詢student的head,hibernate就會在查詢的時候關聯兩張表從而生成的sql就可能只有一條。而如果你設定的是延遲載入,那麼肯定會要生成1+N條sql語句:其中“1”是查詢student的語句,“N”是根據N個student的id去查詢head的N條語句。而且,延遲載入是要用到的時候才去執行查詢,這樣系統判斷那裡需要載入,那裡不需要載入也需要時間,效能上肯定就不如立即載入了!
如果,你是有的地方需要用到student的時候才用到head屬性,那麼你就設定成延遲載入,因為查詢2張表的資料肯定要比查詢1張表的資料消耗大。
到低要怎樣設定就要看你的實際需求了

延遲載入機制是為了避免一些無謂的效能開銷而提出來的,所謂延遲載入就是當在真正需要資料的時候,才真正執行資料載入操作。在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並沒有發起對資料的查詢,如果此時通過一些除錯工具,觀察此時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 whereid=’1’;來查詢資料,並構造目標物件,並且將它賦值到CGLIB$CALBACK_0.target屬性中。
  這樣,通過一箇中間代理物件,Hibernate實現了實體的延遲載入,只有當用戶真正發起獲得實體物件屬性的動作時,才真正會發起資料庫查詢操作。所以實體的延遲載入是用通過中間代理類完成的,所以只有session.load()方法才會利用實體延遲載入,因為只有session.load()方法才會返回實體類的代理類物件。
B、集合型別的延遲載入

  在Hibernate的延遲載入機制中,針對集合型別的應用,意義是最為重大的,因為這有可能使效能得到大幅度的提高,為此Hibernate進行了大量的努力,其中包括對JDKCollection的獨立實現,在一對多關聯中,定義的用來容納關聯物件的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列表,就是所謂的資料索引。當查詢資料索引時,如果沒有找到對應的資料索引,這時就會一條selectSQL的執行,獲得符合條件的資料,並構造實體物件集合和資料索引,然後返回實體物件的集合,並且將實體物件和資料索引納入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根據找到的資料索引發起了兩條selectSQL的查詢操作,這裡造成了對效能的浪費,怎樣才能避免這種情況呢?必須對集合型別中的實體也指定快取策略,對集合型別進行配置:
<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欄位資料真正的讀取操作。

相關推薦

什麼是hibernate延遲載入什麼時候使用載入為什麼載入

所謂懶載入(lazy)就是延時載入,延遲載入。 什麼時候用懶載入呢,我只能回答要用懶載入的時候就用懶載入。 至於為什麼要用懶載入呢,就是當我們要訪問的資料量過大時,明顯用快取不太合適, 因為記憶體容量有限 ,為了減少併發量,減少系統資源的消耗, 我們讓資料在需要的時候才進行載入,這時我們就用到了懶載入。 比

焊接裸銅板客戶錫膏膏體0鹵素的

其他 產生 zha 如果 鹽城 CA 青島 有限公司 .com 焊接裸銅板客戶,錫膏膏體要用0鹵素的 1.錫膏客戶,如果是焊接裸銅板的,一定要下訂單時註明清楚,這種產品要用0鹵素的助焊劑,一定要註意,否則會出現品質問題。0鹵素的助焊劑上錫效果一般,只適合焊接裸銅板,所以不能

Gulp教程之:Gulp能做什麽前端裝逼為何

基本上 什麽 質量 ctrl+ article 方法 靜態 情況下 了解 我們先說說 平時web開發遇到的一些場景 和 苦惱無奈的情況:JavaScript和CSS的版本問題我們都知道 JavaScript和CSS屬於靜態文件,如果地址不變,瀏覽器會緩存這些文件,那就意味著

Gulp教程之:Gulp能做什麼前端裝逼為何

我們先說說 平時web開發遇到的一些場景 和 苦惱無奈的情況: JavaScript和CSS的版本問題 我們都知道 JavaScript和CSS屬於靜態檔案,如果地址不變,瀏覽器會快取這些檔案,那就意味著當我們需要改JavaScript或者CSS檔案的時候,即使我們後端改了,那麼客戶端也是看

Gulp教程之 Gulp能做什麼前端裝逼為何

                我們先說說 平時web開發遇到的一些場景 和 苦惱無奈的情況:JavaScript和CSS的版本問題我們都知道 JavaScript和CSS屬於靜態檔案,如果地址不變,瀏覽器會快取這些檔案,那就意味著當我們需要改JavaScript或者CSS檔案的時候,即使我們後端改了,那麼客

java的設計模式是什麼?為什麼設計模式

1設計模式是在軟體工程實踐過程中,程式設計師們總結出的良好的程式設計方法。使用設計模式能夠增加系統的健壯性,易修改性和可擴充套件性,當你進行開發的軟體規模比較大的時候,良好的設計模式會給程式設計帶來便利,讓系統更加穩定,這些在自己編寫小程式的時候是體現不出來的。現在大多數框架

天天都訊息佇列卻不知道為啥MQ這就有點尷尬了

1、為什麼要使用訊息佇列? 分析:一個用訊息佇列的人,不知道為啥用,有點尷尬。沒有複習這點,很容易被問蒙,然後就開始胡扯了。 回答:這個問題,咱只答三個最主要的應用場景(不可否認還有其他的,但是隻答三個主要的),即以下六個字:解耦、非同步、削峰 (1)解耦 傳統模式:   傳統模式的缺點:

修改登錄填寫緩存戶名的默認背景顏色

ima 用戶 緩存 過渡效果 round class 啟用 textarea span input:-webkit-autofill , textarea:-webkit-autofill, select:-webkit-autofill { -webkit-text

Hibernate延遲載入(查詢優化)關聯級別延遲載入優化策略

1. 類級別延遲載入: 類級別使用延時載入,可以在class標籤上修改是否使用延遲載入 <class name="com.ssh.domain.Customer" table="cst_customer"> 2 關聯級別延遲載入:預設使用到的時候才進行查詢()

很多人在Google Play商店購買或下載APP出現問題例如在你新安裝的系統恢復APP或想安裝心願單中的APPPlay商店出現不能載入等錯誤這實在是太煩人了。 所以我通過搜尋把可

error 491 問題說明: downloads and updates impossible. (不能下載或更新) 解決方案: 進入您的裝置設定,刪除Google賬戶的所有內容。重啟您的Android裝置並重新新增G

架構實戰篇:使用MyBatis延遲載入模式為資料庫減壓附演示例項

一.MyBatis延遲載入 1.延遲載入定義 resultMap可以實現高階對映(使用association、collection實現一對一及一對多對映),association、collection具備延遲載入功能。 需求:如果查詢訂單並且關聯查詢使用者資訊。如果先查詢訂單

C#載入圖片對同一圖片絕對路徑沒問題相對路徑報錯。

讀取圖片時的路徑無外乎有兩種:1:絕對路徑。2:相對路徑。開發過程中絕大多數情況應使用相對路徑。但在讀取圖片時,有時使用相對路徑會報錯,而使用絕對路徑則沒問題。 解決方案:前提是路徑設定正確,在VS中右鍵點選圖片的屬性<複製到輸出目錄>: 不復制------&g

高階對映( 一對多 多對多 延遲載入

<!-- 一對一查詢 --> <resultMap type="cn.labelnet.pojo.OperationCustionMap" id="operationClient"> <id column="id" property="id" /> <re

APPEND 批量載入資料可避免不合格資料

$ sqlplus /nolog SQL*Plus: Release 10.2.0.1.0 - Production on Mon Nov 19 16:50:50 2007 Copyright (c) 1982, 2005, Oracle.  All rights reserved. SQL> con

iOS 同一頁面載入上百張圖片迅速滑動導致記憶體暴漲程式崩潰的參考解決方法

本例中專案大致流程是先由客戶端拍照或者選擇相簿中的圖片進行上傳,然後可以從詳情頁面中瀏覽所有上傳的圖片,由於圖片是按照相簿進行分類,而每個相簿中最多可以有50張照片,極限的情況是詳情頁面最多可以有20多個相簿,由此導致需要對圖片的載入進行必要的優化,避免程式佔用

ExtJS中store自動載入資料的時候在firebug下http status為Aborted的處理方法

本來是一個穩定的功能模組,一直沒有問題,今天在測試資料的時候老是發現載入資料載入失敗,從後臺伺服器的日誌來看,資料已經處理完成,所以和後臺伺服器沒有關係。通過firebug除錯發現,這個ajax請求的status為Aborted,不知道什麼問題導致Aborted這個非標準的

Cocos2d-x3.0 載入Cocostudio的UI後button無法點擊的解決方法

archive nor tar console 大小 接下來 variant set http 近期發現不少朋友都遇到這個問題,用Cocostudio的UI編輯器創建好UI後。在代碼中載入UI,然後給button(Button)加入點擊監聽事件。發現不管怎樣都點擊不了bu

tomcat執行載入機制dns地址解析tcp/ip三握四揮(全)

一.瞭解從輸入url到顯示頁面過程中都發生了什麼:    1.通過url並利用DNS解析出目的主機的ip地址;     2.找到目標主機,建立TCP/IP連線;    3.Tomcat監聽

從零開始學習比特幣開發(四)--網路初始化載入區塊鏈和錢包匯入區塊啟動節點

寫在前面: 本篇文章接續 從零開始學習區塊鏈技術(三)-接入比特幣網路的關鍵步驟解析、建立比特幣錢包,以及重要rpc指令 從零開始學習區塊鏈技術(二)–如何接入比特幣網路以及其原理分析 以及從零開始學習區塊鏈技術(一)–從原始碼編譯比特幣 如果這篇文章看不明白,請務必先閱讀之前的文章

Django專案不能載入靜態資源的問題解決辦法(上)!

    在做個Django試驗的時候發現我直接訪問應用下面的靜態檔案,結果卻是返回了200,但是卻沒有吧靜態問題件載入上來,一個都沒有; 一直百思不得姐,明明檔案都能訪問到了,但是為什麼不能加載出來,最後還是訪問專案下面的靜態檔案給加載出來的! 我的問題復現: