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 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字段數據真正的讀取操做。
相關文章
相關標籤/搜索