熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

集合屬性的延遲加載

2013-11-23 19:12:24  來源: Java核心技術 

  Hibernae 的延遲加載是一個非常常用的技術實體的集合屬性默認會被延遲加載實體所關聯的實體默認也會被延遲加載Hibernate 通過這種延遲加載來降低系統的內存開銷從而保證 Hibernate 的運行性能
   
    下面先來剖析 Hibernate 延遲加載的秘密
   
    集合屬性的延遲加載
   
    當 Hibernate 從數據庫中初始化某個持久化實體時該實體的集合屬性是否隨持久化類一起初始化呢?如果集合屬性裡包含十萬甚至百萬的記錄在初始化持久化實體的同時完成所有集合屬性的抓取將導致性能急劇下降完全有可能系統只需要使用持久化類集合屬性中的部分記錄而完全不是集合屬性的全部這樣沒有必要一次加載所有的集合屬性
   
    對於集合屬性通常推薦使用延遲加載策略所謂延遲加載就是等系統需要使用集合屬性時才從數據庫裝載關聯的數據
   
    例如下面 Person 類持有一個集合屬性該集合屬性裡的元素的類型為 Address該 Person 類的代碼片段如下
   
    清單 Personjava
   
    public class Person
   
      {
   
      // 標識屬性
   
      private Integer id;
   
      // Person 的 name 屬性
   
      private String name;
   
      // 保留 Person 的 age 屬性
   
      private int age;
   
      // 使用 Set 來保存集合屬性
   
      private Set addresses = new HashSet()
   
      // 下面省略了各屬性的 setter 和 getter 方法
   
      …
   
      }
   
    為了讓 Hibernate 能管理該持久化類的集合屬性程序為該持久化類提供如下映射文件
   
    清單 Personhbmxml
   
    xml version= encoding=GBK?>
   
      DOCTYPE hibernatemapping PUBLIC 
   
    //Hibernate/Hibernate Mapping DTD //EN
   
    /dtd/hibernatemappingdtd>
   
      <hibernatemapping package=orgcrazyitappdomain>
   
   
   
    <class name=Person table=person_inf>
   
   
   
      <id name=id column=person_id>
   
   
   
      <generator class=identity/>
   
      id>
   
   
   
      <property name=name type=string/>
   
      <property name=age type=int/>
   
   
   
      <set name=addresses table=person_address lazy=true>
   
   
   
      <key column=person_id/>
   
      <compositeelement class=Address>
   
   
   
      <property name=detail/>
   
   
   
      <property name=zip/>
   
      compositeelement>
   
      set>
   
      class>
   
      hibernatemapping>
   
    從上面映射文件的代碼可以看出Person 的集合屬性中的 Address 類只是一個普通的 POJO該 Address 類裡包含 detailzip 兩個屬性由於 Address 類代碼非常簡單故此處不再給出該類的代碼
   
    上面映射文件中 元素裡的代碼指定了 lazy=true(對於 元素來說lazy=true是默認值)它指定 Hibernate 會延遲加載集合屬性裡 Address 對象
   
    例如通過如下代碼來加載 ID 為 的 Person 實體
   
    Session session = sfgetCurrentSession()
   
      Transaction tx = sessionbeginTransaction()
   
      Person p = (Person) sessionget(Personclass   //<>
   
      Systemoutprintln(pgetName())
   
    上面代碼只是需要訪問 ID 為 的 Person 實體並不想訪問這個 Person 實體所關聯的 Address 對象此時有兩種情況
   
    如果不延遲加載Hibernate 就會在加載 Person 實體對應的數據記錄時立即抓取它關聯的 Address 對象
   
    如果采用延遲加載Hibernate 就只加載 Person 實體對應的數據記錄
   
    很明顯第二種做法既能減少與數據庫的交互而且避免了裝載 Address 實體帶來的內存開銷這也是 Hibernate 默認啟用延遲加載的原因
   
    現在的問題是延遲加載到底是如何實現的呢? Hibernate 在加載 Person 實體時Person 實體的 addresses 屬性值是什麼呢?


   
    為了解決這個問題我們在 <>號代碼處設置一個斷點在 Eclipse 中進行 Debug此時可以看到 Eclipse 的 Console 窗口有如圖 所示的輸出
   
    圖 延遲加載集合屬性的 Console 輸出




  

  正如圖 輸出所看到的此時 Hibernate 只從 Person 實體對應的數據表中抓取數據並未從 Address 對象對應的數據表中抓取數據這就是延遲加載
   
    那麼 Person 實體的 addresses 屬性是什麼呢?此時可以從 Eclipse 的 Variables 窗口看到如圖 所示的結果
   
    圖 延遲加載的集合屬性值

  

  從圖 的方框裡的內容可以看出這個 addresses 屬性並不是我們熟悉的 HashSetTreeSet 等實現類而是一個 PersistentSet 實現類這是 Hibernate 為 Set 接口提供的一個實現類
   
    PersistentSet 集合對象並未真正抓取底層數據表的數據因此自然也無法真正去初始化集合裡的 Address 對象不過 PersistentSet 集合裡持有一個 session 屬性這個 session 屬性就是 Hibernate Session當程序需要訪問 PersistentSet 集合元素時PersistentSet 就會利用這個 session 屬性去抓取實際的 Address 對象對應的數據記錄
   
    那麼到底抓取那些 Address 實體對應的數據記錄呢?這也難不倒 PersistentSet因為 PersistentSet 集合裡還有一個 owner 屬性該屬性就說明了 Address 對象所屬的 Person 實體Hibernate 就會去查找 Address 對應數據表中外鍵值參照到該 Person 實體的數據
   
    例如我們單擊圖 所示窗口中 addresses 行也就是告訴 Eclipse 要調試輸出 addresses 屬性這就是要訪問 addresses 屬性了此時就可以在 Eclipse 的 Console 窗口看到輸出如下 SQL 語句
   
    select addresses_person_id as person___ addresses_detail as detail_ addresses_zip as zip_
   
    from person_address addresses_
   
    where addresses_person_id=?
   
    這就是 PersistentSet 集合跟據 owner 屬性去抓取特定 Address 記錄的 SQL 語句此時可以從 Eclipse 的 Variables 窗口看到圖 所示的輸出
   
    圖 已加載的集合屬性值

  

   

  從圖 可以看出此時的 addresses 屬性已經被初始化了集合裡包含了 個 Address 對象這正是 Person 實體所關聯的兩個 Address 對象
   
    通過上面介紹可以看出Hibernate 對於 Set 屬性延遲加載關鍵就在於 PersistentSet 實現類在延遲加載時開始 PersistentSet 集合裡並不持有任何元素但 PersistentSet 會持有一個 Hibernate Session它可以保證當程序需要訪問該集合時立即去加載數據記錄並裝入集合元素
   
    與 PersistentSet 實現類類似的是Hibernate 還提供了 PersistentListPersistentMapPersistentSortedMapPersistentSortedSet 等實現類它們的功能與 PersistentSet 的功能大致類似
   
    熟悉 Hibernate 集合屬性讀者應該記得Hibernate 要求聲明集合屬性只能用 SetListMapSortedSetSortedMap 等接口而不能用 HashSetArrayListHashMapTreeSetTreeMap 等實現類其原因就是因為 Hibernate 需要對集合屬性進行延遲加載而 Hibernate 的延遲加載是依靠 PersistentSetPersistentListPersistentMapPersistentSortedMapPersistentSortedSet 來完成的也就是說Hibernate 底層需要使用自己的集合實現類來完成延遲加載因此它要求開發者必須用集合接口而不是集合實現類來聲明集合屬性
   
    Hibernate 對集合屬性默認采用延遲加載在某些特殊的情況下 等元素設置 lazy=false屬性來取消延遲加載

   

   



From:http://tw.wingwit.com/Article/program/Java/hx/201311/26434.html
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.