|
延遲初始 Lazy Initialization
首先我們來看看這個主題: 入門 11 - Set 映射 依這個主題所完成的例子,請將Hibernate的show_sql設(shè)定為true,當(dāng)我們使用下面的程序時,觀看控制臺所使用的SQL: HibernateTest.java import 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運(yùn)作的例子如下: 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表格中讀取數(shù)據(jù)之外,還向ADDRS表格讀取數(shù)據(jù),預(yù)設(shè)上,Hibernate會將所有關(guān)聯(lián)到的對象,透過一連串的SQL語句讀取并加載數(shù)據(jù),然而現(xiàn)在考慮一種情況,我們只是要取得所有USER的名稱,而不用取得它們的郵件地址,此時自動讀取相關(guān)聯(lián)的對象就是不必要的。 在Hibernate中,集合類的映像可以延遲初始(Lazy Initialization),也就是在真正索取該對象的數(shù)據(jù)時,才向數(shù)據(jù)庫查詢,就這個例子來說,就是我們在讀取User時,先不取得其中的 addrs屬性中之對象數(shù)據(jù),由于只需要讀取User的name屬性,此時我們只要執(zhí)行一次select即可,真正需要addrs的數(shù)據(jù)時,才向數(shù)據(jù)庫要求。 要使用Hibernate的延遲初始功能,只要在集合類映像時,加上lazy="true"即可,例如在我們的User.hbm.xml中的<set>中如下設(shè)定: User.hbm.xml <set name="addrs" table="ADDRS" lazy="true"> <key column="USER_ID"/> <element type="string" column="ADDRESS" not-null="true"/> </set> 我們來看看下面這個程序: HibernateTest.java import 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(); } } 在沒有使用延遲初始時,控制臺會顯示以下的內(nèi)容: 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 如果使用延遲初始,則會出現(xiàn)以下的內(nèi)容: 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語句出現(xiàn)的位置,在使用延遲初始功能前,會將所有相關(guān)聯(lián)到的數(shù)據(jù)一次查完,而使用了延遲初始之后,只有在真正需要addrs的數(shù)據(jù)時,才會使用SQL查詢相關(guān)數(shù)據(jù)。 Hibernate實(shí)現(xiàn)延遲初始功能的方法,是藉由實(shí)現(xiàn)一個代理對象(以Set來說,其實(shí)現(xiàn)的代理子類是 net.sf.hibernate.collection.Set),這個代理類實(shí)現(xiàn)了其所代理之對象之相關(guān)方法,每個方法的實(shí)現(xiàn)實(shí)際上是委托(delegate)真正的對象,查詢時加載的是代理對象,在真正呼叫對象的相關(guān)方法之前,不會去初始真正的對象來執(zhí)行被呼叫的方法。 所以為了能使用延遲初始,您在使用集合映像時,在宣告時必須是集合類的接口,而不是具體的實(shí)現(xiàn)類(例如宣告時使用Set,而不是HashSet)。 使用延遲初始的一個問題是,由于在需要時才會去查詢數(shù)據(jù)庫,所以session不能關(guān)閉,如果在session關(guān)閉后,再去要求被關(guān)聯(lián)的對象,將會發(fā)生LazyInitializationException,像是: Set addrs = user.getAddrs(); session.close(); // 下面這句會發(fā)生LazyInitializationException Object[] addrs = user.getAddrs().toArray(); 如果您使用了延遲初始,而在某些時候仍有需要在session關(guān)閉之后取得相關(guān)對象,則可以使用Hibernate.initialize()來先行加載相關(guān)對象,例如: Hibernate.initialize(user.getAddrs()); session.close(); Set add = user.getAddrs(); Object[] addo = user.getAddrs().toArray(); 延遲初始只是Hibernate在取得數(shù)據(jù)時的一種策略,目的是為了調(diào)節(jié)數(shù)據(jù)庫存取時的時機(jī)以取得一些效能,除了延遲初始之外,還有其它的策略來調(diào)整數(shù)據(jù)庫存取的方法與時機(jī),這部份牽涉的討論范圍很大,有興趣的話,可以參考Hibernate in Action的4.4.5。 |
|
|