2011年3月23日 星期三

Hibernate 教學 - Lazy 延遲加載

Lazy,稱為延遲加載 (或稱懶加載),通常多用於集合的抓取策略

簡言之就是在需要的情況下才會發出 SQL 語句,將所需的資料取出。

在說到延遲加載時,會加減牽涉到快取的問題,以下再一一說明

首先以集合的 set 為例,其 lazy 屬性預設為 true,以提高較好的效能

如下所示  :

<set name="assets" order-by="id desc" lazy="false">
          <key column="assettype_id" />
          <one-to-many class="org.pojo.Asset" />
</set>

以下先以一個簡短的程式來介紹 Session 的 get() 和 load() 方法

首先以一個多對一的範例為主 Assettype 為一,Asset 為多 底下先看看他們的映射文件

Asset.hbm.xml

<hibernate-mapping>
  <class name="org.pojo.Asset" table="t_asset" >
      <id name="id">
          <generator class="native" />

      </id>
     
      <property name="name" />
      <property name="price" />
      <property name="verifystatus" />
      <property name="remark" />
      <many-to-one name="type" column="assettype_id" not-null="true"/>
</class>
</hibernate-mapping>


Assettype.hbm.xml

<hibernate-mapping>
  <class name="org.pojo.Assettype" table="t_assettype">
      <id name="id">
         <generator class="native" />
      </id>
     
      <property name="name" />

      <set name="assets" order-by="id desc" lazy="true">
          <key column="assettype_id" />
          <one-to-many class="org.pojo.Asset" />
      </set>
  </class>
</hibernate-mapping>

最後以 JUnit 來測試一下

public void testsetlazy()
{
    SessionFactory factory = HibernateUtil.getSessionFactory();
    Session session = factory.openSession();

    //以 load 加載 id 為 1 的 Assettype
    Assettype type = (Assettype)session.load(Assettype.class, 1);  //line 1
    
    int id = type.getId();   //line 2
    String name = type.getName();   //line 3

    Set assets = type.getAsset();  //line 4
    for(Iterator iterator = assets.iterator();iterator.hasNext();) //line 5 
    {
        Asset asset = (Asset)iterator.next();
        System.out.println(asset.getName());
    }
}

以上面的簡短程式來示範 load() 的加載過程,

注意 Assettype.hbm.xml 的 set 集合的 lazy 屬性是設為 true(預設)

接著在以上程式中的

line 1, Hibernate 執行 load() 撈出實體時,是不會發出SQL查詢語句的

也就是說 Hibernate 並不會到資料庫裡撈出該筆對應的資料

line 2 呢? 答案也是不會的,Hibernate 同樣也是不會去資料庫裡撈出資料

因為在 line 2 執行的 getId(), 其實你在 line 1 已經告訴 Hibernate 這個 Assettype 的 id 屬性了

所以 Hibernate 當然就不會再去查詢資料了

line 3 的話,就會發出 SQL 查詢語句 以查詢出 Assettype 的 name 屬性

line 4 勒?  這是重點了,答案是不會發出 SQL 查詢語句,為什麼呢

首先因為在 Assttype.hbm.xml 的 set 的 lazy 屬性設定為懶加載,所以當你的程式

要用到該集合的時候才會執行 SQL 語句去查詢對應的 Asset ,但是注意一點的是

Set assets = type.getAsset();   這行並不代表你有要使用它(Asset),

而真正使用的時候則是再下一行,所以

line 5 的話就會產生一條 SQL 查詢語句去查詢目前 Assettype 所對應到的所有 Asset

-------------------------------------------------------------------------------------

到這邊應該能夠了解 load() 了吧,那如果把 line 1 改成用 get() 來撈出實體呢?

那麼 line 1 應該會發出一條的SQL查詢語句了

也就是說 Hibernate 會去資料庫撈出對應資料(Assettype)

line 2 line 3 都不會再發出任何查詢語句

因為在 line 1 時,Hibernate 已經將 Assettype 實體撈出了

而接下來再 line 4 line 5 的解釋,則跟上面的 load() 範例一樣

--------------------------------------------------------------------------------------

到這邊以上的測試都是以 lazy 屬性為 true 而言,那如果把 lazy 設定為 false 呢

這裡以 load() 為例,

當然 line 1 還是不會發出執行 SQL 的但是到了下一行

line 2 時,他會發出兩條 SQL 的查詢語句 !!   為什麼?

注意,我們是在 set 集合上設定他不要延遲加載也就是告訴了 Hibernate


如果要加載 Assettype 這個實體時,遇到 set 的 assets 屬性時


也請將集合所對應到的資料一並撈出,所以 Hibernate 除了發出查詢 Assettype 的


SQL查詢語句之外,還會再發出一條查詢集合所對應到的 Asset 語句

而這個情況正就是因為你在 set 集合的 lazy 屬性設定為 false 的效果

所以接下去的程式皆不會再發出任何 SQL 語句了

最後如果在 line 1 以 get() 為例來示範 lazy = "false" 時

那麼很清楚的知道 在 line 1 執行完後就會發出兩條 SQL 語句了

所以以上除了可以了解 lazy 的意涵

也能知道 get 和 load 的差別最主要就是

在 get 的時候就會直接從資料庫中將該實體撈出

而 load 則不會

沒有留言:

張貼留言