Hibernate lazy延迟加载


一、延迟加载的概念

当Hibernate从数据库中加载某个对象时,不加载关联的对象,而只是生成了代理对象,获取使用session中的load的方法(在没有改变lazy属性为false的情况下)获取到的也是代理对象,所以在上面这几种场景下就是延迟加载。

二、理解立即加载的概念

当Hibernate从数据库中加载某个对象时,加载关联的对象,生成的实际对象,获取使用session中的get的方法获取到的是实际对象。

三、为什么要使用延迟加载

延迟加载策略能避免加载应用程序不需要访问的关联对象,以提高应用程序的性能。

四、立即加载的缺点

Hibernate在查询某个对象时,立即查询与之关联的对象,我们可以看出这种加载策略存在两大不足:

  1. select的语句数目太多,需要频繁的访问数据库,会影响查询的性能。
  2. 在应用程序只需要访问要的对象,而不需要访问与他关联的对象的场景下,加载与之关联的对象完全是多余的操作,这些多余的操作是会占内存,这就造成了内存空间的浪费。

五、什么时候使用延迟加载什么时候使用立即加载

如果程序加载一个持久化对象的目的是为访问他的属性,则可以采用立即加载。如果程序加载一个持久化对象的目的仅仅是为了获得他的引用,则可以采用延迟加载。

六、Hibernate在对象-关系映射中配置加载策略

类级别:

      <class>元素中lazy属性的可选值为true(延迟加载)和false(立即加载);

      <class>元素中的lazy属性的默认值为true

一对多关联级别:

      <set>元素中的lazy属性的可选值为:true(延迟加载),extra(增强延迟加载)和false(立即加载);

      <set>元素中的lazy属性的默认值为true

多对一关联级别:

      <many-to-one>元素中lazy属性的可选值为:proxy(延迟加载),no-proxy(无代理延迟加载)和false(立即加载)

      <many-to-one>元素中的lazy属性的默认值为proxy

七、演示各种方式的延迟加载配置

下面的一些案例中都会以订单(Order)的客户(Customer)的例子讲解:

订单和客户是多对一的关系

关于订单和客户的Hibernate配置参考之前文章:

http://www.yiidian.com/hibernate/hibernate-one2many.html

7.1、类级别的查询策略:

   01.立即加载案例:

     需求:通过Session的load()方法加载Customer对象时:

     首先在Customer.hbm.xml文件中配置lazy属性为false,表示立即加载。


 

@Test
	public void loadCustomer() {
		// 获取Session对象
		Session session = HibernateUtil.getSession();
		// 如果通过load方式加载Customer对象
		Customer customer = (Customer) session.load(Customer.class, 1);
		// 关闭session
		session.close();
	}

我们知道使用load方法加载的是代理对象,只会在属性里保存一个OID,但是如果在Customer映射文件中配置了类级别的lazy为false就代表加载该对象时立即加载,也就是立即检索一道数据库,发出sql:

Hibernate: 
    select
        customer0_.id as id1_0_0_,
        customer0_.name as name2_0_0_,
        customer0_.gender as gender3_0_0_ 
    from
        t_customer customer0_ 
    where
        customer0_.id=?

     02.延迟加载案例:

     同样是获取Customer对象,但是要把Customer.hbm.xml配置文件的类级别的lazy属性改为lazy=”true” 或者不写,应为类级别的lazy属性默认就是true,

@Test
	public void loadCustomer() {
		// 获取Session对象
		Session session = HibernateUtil.getSession();
		// 如果通过load方式加载Customer对象
		Customer customer = (Customer) session.load(Customer.class, 1);
		// 关闭session
		session.close();
	}

此时Customer.hbm.xml配置文件的类级别的lazy属性为true,则是延迟加载,那么load方法获取的知识Customer的代理对象,所以他不会去检索数据库。

7.2、一对多和多对多关联的查询策略

  01.立即加载案例:

在获取客户对象的时候同时获取订单对象:

设置Customer.hbm.xml 类级别的lazy属性为false;表示立即加载:

设置<set>元素的lazy属性为false,表示在加载客户的同时立即加载订单:

	@Test
	public void loadCustomer() {
		// 获取Session对象
		Session session = HibernateUtil.getSession();
		// 如果通过load方式加载Customer对象
		Customer customer = (Customer) session.load(Customer.class, 1);
		// 关闭session
		session.close();
	}

控制台输出结果:

Hibernate: 
    select
        customer0_.id as id1_0_0_,
        customer0_.name as name2_0_0_,
        customer0_.gender as gender3_0_0_ 
    from
        t_customer customer0_ 
    where
        customer0_.id=?
Hibernate: 
    select
        orders0_.cust_id as cust_id4_1_0_,
        orders0_.id as id1_1_0_,
        orders0_.id as id1_1_1_,
        orders0_.orderno as orderno2_1_1_,
        orders0_.product_name as product_3_1_1_,
        orders0_.cust_id as cust_id4_1_1_ 
    from
        t_order orders0_ 
    where
        orders0_.cust_id=?

当你想获取一的一方(Customer)的对象同时,你也要加载多的一方(Order)的对象,那么你要在一的一方(Customer)的<set>的节点上加上lazy="false"表示立即加载,所以在使用load方式加载Customer对象的时候,Order对象也会加载出来,所以程序在运行到

Customer customer = (Customer) session.load(Customer.class, 1);

会发出两条sql语句:

第一条是查询部门的信息,第二条sql是根据部门编号去数据库中检索员工信息。

  

   02.延迟加载:

如果把上面的案例<set>节点的属性lazy改为true,或者默认不写,那么在加载Customer对象的时候,就不会再去加载Order对象,而且只会发出一条sql,这条sql就是指检索客户的信息。

Hibernate: 
    select
        customer0_.id as id1_0_0_,
        customer0_.name as name2_0_0_,
        customer0_.gender as gender3_0_0_ 
    from
        t_customer customer0_ 
    where
        customer0_.id=?

     

   03.增强延迟加载:

   当<set>元素中配置lazy的属性为extra,表明是增强延迟加载策略。

其实增强延迟加载策略与一般的延迟加载策略lazy="true"非常相似。他们主要区别在于,我们看到这个名词增强延迟加载,顾名思义就是这个策略能在进一步的帮我延迟加载这个对象,也就是代理对象的初始化时机。

     演示案例:

         01. 当set节点的lazy属性为true,或者不写的话(取默认值),那么执行以下语句:  

@Test
	public void loadCustomer2() {
		// 获取Session对象
		Session session = HibernateUtil.getSession();
		// 如果通过load方式加载Customer对象
		Customer customer = (Customer) session.load(Customer.class, 1);
		customer.getOrders().size();
		// 关闭session
		session.close();
	}

控制台输出结果:

        02. 当set节点的lazy属性为extra那么执行以下语句:

 

7.3、多对一关联的查询策略

在映射文件中,<many-to-one>元素用来设置多对一的关系,在Order.hbm.xml文件中表明Order类到Customer类的多对一的关联关系:

      01.延迟加载

       需求:获取Order对象,但是并不去加载与之关联的Customer对象。

       首先要设置<many-to-one>节点的lazy属性为proxy,表示延迟加载。

@Test
	public void loadOrder() {
		// 获取Session对象
		Session session = HibernateUtil.getSession();
		// 如果通过load方式加载Order对象
		Order order = (Order) session.get(Order.class, 1);

		// 获取Customer对象,因为此时的配置文件lazy是proxy,所以是代理对象
		Customer customer = order.getCustomer();
		// 关闭session
		session.close();
	}

结果大家可想而知:<many-to-one>节点的lazy属性为proxy,表示延迟加载。在加载Order对象的时候会发出sql去查询数据库,但是在获取Customer对象的时候延迟加载了,所以不会发出sql。

       02.无代理延迟加载:

在<many-to-one>元素中配置lazy属性为no-proxy,表示无代理延迟加载。

@Test
	public void loadOrder() {
		// 获取Session对象
		Session session = HibernateUtil.getSession();
		// 如果通过load方式加载Order对象
		Order order = (Order) session.get(Order.class, 1);

		// 获取Customer对象
		Customer customer = order.getCustomer();
		// 关闭session
		session.close();
	}


此程序在加载的Order对象customer属性为NULL,当程序运行到第3行的时候将触发Hibernate执行查询Customer表的select语句,从而加载Dept对象,由此可见,当lazy属性为proxy时,可以延长延迟加载Dept代理对象的时间,而lazy属性为no-proxy时,则可以避免使用由Hibernate提供的Dept代理类实例,是Hibernate对程序提供更加透明的持久化服务。

     03.立即加载:

首先要设置<many-to-one>节点的lazy属性为false,表示立即加载。      

@Test
	public void loadOrder2() {
		// 获取Session对象
		Session session = HibernateUtil.getSession();
		// 如果通过load方式加载Order对象
		Order order = (Order) session.get(Order.class, 1);

		////获取Dept对象,因为此时的配置文件lazy是false,所以是实际对象
		Customer customer = order.getCustomer();
		// 关闭session
		session.close();
	}

控制台输出结果:

Hibernate: 
    select
        order0_.id as id1_1_0_,
        order0_.orderno as orderno2_1_0_,
        order0_.product_name as product_3_1_0_,
        order0_.cust_id as cust_id4_1_0_ 
    from
        t_order order0_ 
    where
        order0_.id=?
Hibernate: 
    select
        customer0_.id as id1_0_0_,
        customer0_.name as name2_0_0_,
        customer0_.gender as gender3_0_0_ 
    from
        t_customer customer0_ 
    where
        customer0_.id=?

源码下载:https://pan.baidu.com/s/1miKLeQW