Hibernate入门24 - 延迟初始 Lazy Initialization

来源:互联网 发布:阿里御膳房数据体系 编辑:程序博客网 时间:2024/06/05 21:08

入门 24 - 延迟初始 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。

原创粉丝点击