`
hunterlucas
  • 浏览: 28137 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

关于hibernate的Lazy Initialization

阅读更多

转自:http://www.javaworld.com.tw/confluence/pages/viewpage.action?pageId=1840

首先我們來看看這個主題:


 依這個主題所完成的例子,請將Hibernate的show_sql設定為true,當我們使用下面的程式時,觀看控制台所使用的SQL:

HibernateTest.javaimport onlyfun.caterpillar.*;
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
import java.util.*;

public class HibernateTest {
    public static void main(String[] args) throws HibernateException {
        SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
        Session session = sessionFactory.openSession();
       
        List users = session.find("from User");

        session.close();
        sessionFactory.close();
    }
}

 SQL運作的例子如下:

Hibernate: select user0_.USER_ID as USER_ID, user0_.NAME as NAME from USER user0_
Hibernate: select addrs0_.USER_ID as USER_ID__, addrs0_.ADDRESS as ADDRESS__ from ADDRS addrs0_ where addrs0_.USER_ID=?
Hibernate: select addrs0_.USER_ID as USER_ID__, addrs0_.ADDRESS as ADDRESS__ from ADDRS addrs0_ where addrs0_.USER_ID=?


 可以看到的,除了從USER表格中讀取資料之外,還向ADDRS表格讀取資料,預設上,Hibernate會將所有關聯到的物件,透過一連串的SQL語句讀取並載入資料,然而現在考慮一種情況,我們只是要取得所有USER的名稱,而不用取得它們的郵件位址,此時自動讀取相關聯的物件就是不必要的。

 在Hibernate中,集合類的映射可以延遲初始(Lazy Initialization),也就是在真正索取該物件的資料時,才向資料庫查詢,就這個例子來說,就是我們在讀取User時,先不取得其中的 addrs屬性中之物件資料,由於只需要讀取User的name屬性,此時我們只要執行一次select即可,真正需要addrs的資料時,才向資料庫要求。

 要使用Hibernate的延遲初始功能,只要在集合類映射時,加上lazy="true"即可,例如在我們的User.hbm.xml中的<set>中如下設定:

User.hbm.xml

<set name="addrs" table="ADDRS" lazy="true">
            <key column="USER_ID"/>
            <element type="string" column="ADDRESS" not-null="true"/>
</set>

 

 我們來看看下面這個程式:

HibernateTest.javaimport onlyfun.caterpillar.*;
import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;
import java.util.*;

public class HibernateTest {
    public static void main(String[] args) throws HibernateException {
        SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
        Session session = sessionFactory.openSession();
       
        List users = session.find("from User");

        for (ListIterator iterator = users.listIterator(); iterator.hasNext(); ) {
            User user = (User) iterator.next();
            System.out.println(user.getName());
            Object[] addrs = user.getAddrs().toArray();
           
            for(int i = 0; i < addrs.length; i++) {
                System.out.println("\taddress " + (i+1) + ": " + addrs[i]);   
            }
        }

        session.close();
        sessionFactory.close();
    }
}


 在沒有使用延遲初始時,控制台會顯示以下的內容:

Hibernate: select user0_.USER_ID as USER_ID, user0_.NAME as NAME from USER user0_
Hibernate: select addrs0_.USER_ID as USER_ID__, addrs0_.ADDRESS as ADDRESS__ from ADDRS addrs0_ where addrs0_.USER_ID=?
Hibernate: select addrs0_.USER_ID as USER_ID__, addrs0_.ADDRESS as ADDRESS__ from ADDRS addrs0_ where addrs0_.USER_ID=?
caterpillar
   address 1: caterpillar@caterpillar.onlyfun.net
   address 2: justin@caterpillar.onlyfun.net
   address 3: justin@fake.com
momor
   address 1: momor@fake.com
   address 2: momor@caterpillar.onlyfun.net


 如果使用延遲初始,則會出現以下的內容:

Hibernate: select user0_.USER_ID as USER_ID, user0_.NAME as NAME from USER user0_
caterpillar
Hibernate: select addrs0_.USER_ID as USER_ID__, addrs0_.ADDRESS as ADDRESS__ from ADDRS addrs0_ where addrs0_.USER_ID=?
   address 1: caterpillar@caterpillar.onlyfun.net
   address 2: justin@caterpillar.onlyfun.net
   address 3: justin@fake.com
momor
Hibernate: select addrs0_.USER_ID as USER_ID__, addrs0_.ADDRESS as ADDRESS__ from ADDRS addrs0_ where addrs0_.USER_ID=?
   address 1: momor@fake.com
   address 2: momor@caterpillar.onlyfun.net


 請注意SQL語句出現的位置,在使用延遲初始功能前,會將所有相關聯到的資料一次查完,而使用了延遲初始之後,只有在真正需要addrs的資料時,才會使用SQL查詢相關資料。

 Hibernate實現延遲初始功能的方法,是藉由實現一個代理物件(以Set來說,其實現的代理子類是 net.sf.hibernate.collection.Set),這個代理類實現了其所代理之物件之相關方法,每個方法的實現實際上是委托(delegate)真正的物件,查詢時載入的是代理物件,在真正呼叫物件的相關方法之前,不會去初始真正的物件來執行被呼叫的方法。

 所以為了能使用延遲初始,您在使用集合映射時,在宣告時必須是集合類的介面,而不是具體的實現類(例如宣告時使用Set,而不是HashSet)。

 使用延遲初始的一個問題是,由於在需要時才會去查詢資料庫,所以session不能關閉,如果在session關閉後,再去要求被關聯的物件,將會發生LazyInitializationException,像是:

Set addrs = user.getAddrs();
session.close();



// 下面這句會發生LazyInitializationException

 

Object[] addrs = user.getAddrs().toArray();

 

如果您使用了延遲初始,而在某些時候仍有需要在session關閉之後取得相關物件,則可以使用Hibernate.initialize()來先行載入相關物件,例如:

Hibernate.initialize(user.getAddrs());     
session.close();
Set add = user.getAddrs();
Object[] addo = user.getAddrs().toArray();

 

延遲初始只是Hibernate在取得資料時的一種策略,目的是為了調節資料庫存取時的時機以取得一些效能,除了延遲初始之外,還有其它的策略來調整資料庫存取的方法與時機,這部份牽涉的討論範圍很大,有興趣的話,可以參考Hibernate in Action的4.4.5。

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics