延迟初始 Lazy Initialization
首先我们来看看这个主题: 入门 11 - Set 映射 依这个主题所完成的例子,请将Hibernate的show_sql设定为true,当我们使用下面的程序时,观看控制台所使用的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运作的例子如下: 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.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(); } } 在没有使用延迟初始时,控制台会显示以下的内容: 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。 |
|
来自: WindySky > 《Hibernate入门》