hibernate延迟加载分析


在hibernate中我们知道如果要从数据库中得到一个对象,通常有两种方式,一种是通过session.get()方法,另一种就是通过session.load()方法,然后其实这两种方法在获得一个实体对象时是有区别的,在查询性能上两者是不同的。

一.load加载方式

当使用load方法来得到一个对象时,此时hibernate会使用延迟加载的机制来加载这个对象,即:当我们使用session.load()方法来加载一个对象时,此时并不会发出sql语句,当前得到的这个对象其实是一个代理对象,这个代理对象只保存了实体对象的id值,只有当我们要使用这个对象,得到其它属性时,这个时候才会发出sql语句,从数据库中去查询我们的对象

session = HibernateUtil.getSession();
Type type = (Type) session.load(Type.class, 9);

通过load的方式加载对象时,会使用延迟加载机制,此时并不会发出sql语句,只有当我们需要使用的时候才会从数据库中去查询

请看下面的一段代码:

@Test
	public void testLoad() {
		Session session = null;
		try {
			session = HibernateUtil.getSession();
			Type type = (Type) session.load(Type.class, 9);
                        System.out.println("load加载 时 类型 "+ type.getClass());
			Type t = (Type) session.get(Type.class, 11);
			System.out.println("get加载 时 类型 "+t.getClass());
			System.out.println("load 加载 sql 语句的发送时间 ------");
			System.out.println(type.getName());

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (session != null) {
				session.close();
			}
		}
	}

请看执行结果:

load加载 时 类型 class com.lgh.hibernate.entity.Type_$$_jvstd0_0
Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
get加载 时 类型 class com.lgh.hibernate.entity.Type
load 加载 sql 语句的发送时间 ------
Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
水果

通过结果我们发现     当我们使用session.load()方法来加载一个对象时,此时并不会发出sql语句,当前得到的这个对象其实是一个代理对象,这个代理对象只保存了实体对象的id值,实际上我们得到的是hibernate 帮我们动态生成的Type 子类 ,如果我们要使用该对象时获取除ID的其他属性时,此时我们看到控制台会发出了sql查询语句,会将该对象从数据库中查询出来

二、get加载方式

相对于load的延迟加载方式,get就直接的多,当我们使用session.get()方法来得到一个对象时,不管我们使不使用这个对象,此时都会发出sql语句去从数据库中查询出来

@Test
	public void testGet() {
		Session session = null;
		try {
			session = HibernateUtil.getSession();
			
			Type t = (Type) session.get(Type.class, 11);
			System.out.println("get加载 时 类型 "+t.getClass());

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (session != null) {
				session.close();
			}
		}
	}

Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
get加载 时 类型 class com.lgh.hibernate.entity.Type

get与load 的 另一些区别

get 和load 在加载方式上的不同 也使它们查询ID不存在的对象时所引发异常也不相同

如果使用get方式来加载对象,当我们试图得到一个id不存在的对象时,此时会报NullPointException的异常

Type t = (Type) session.get(Type.class, 3434);
System.out.println("get加载 时 类型 "+t.getClass());



Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
java.lang.NullPointerException
	at com.lgh.hibernate.test02.HibernateCache.testGet(HibernateCache.java:121)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
         ....

这是因为通过get方式我们会去数据库中查询出该对象,但是这个id值不存在,所以此时Type对象是null,所以就会报NullPointException的异常了。

如果使用load方式来加载对象,当我们试图得到一个id不存在的对象时,此时会报ObjectNotFoundException异常

                        session = HibernateUtil.getSession();
			
			Type t = (Type) session.load(Type.class, 3434);
			System.out.println("load加载 时 类型 "+t.getClass());
			System.out.println(t.getName());

load加载 时 类型 class com.lgh.hibernate.entity.Type_$$_jvstd0_0
Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.lgh.hibernate.entity.Type#3434]

为什么使用load的方式和get的方式来得到一个不存在的对象报的异常不同呢??其原因还是因为load的延迟加载机制,使用load时,此时的Type对象是一个代理对象,仅仅保存了当前的这个id值,当我们试图得到该对象的name属性时,这个属性其实是不存在的,所以就会报出ObjectNotFoundException这个异常了。

我们在编写javaweb 项目 时通常 都采用分层写法 ,把像操作数据库的的一些方法封装到一个类中,统一管理 ,一般我们都把它们命名为Dao 层 ,现在我们模拟一个TypeDao

类的getType()方法

package com.lgh.hibernate.test02;

import org.hibernate.Session;

import com.lgh.hibernate.entity.Type;
import com.lgh.hibernate.util.HibernateUtil;

public class TypeDao {

    public static void main(String[] args) {
         TypeDao hc = new TypeDao();
         Type t =    hc.getType();
         System.out.println("----"+t.getName());

    }

    public Type getType(){
        Session session = null;
        try {
            session = HibernateUtil.getSession();
        
            Type t = (Type) session.load(Type.class, 11);
            return t;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (session != null) {
                session.close();
            }
        }
        return null;
    }
}


模拟了一个TypeDao这样的对象,然后我们在测试用例里面来通过load加载一个对象,此时我们发现控制台会报org.hibernate.LazyInitializationException: could not initialize proxy - no Session

Exception in thread "main" org.hibernate.LazyInitializationException: could not initialize proxy - no Session
	at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165)
	at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:286)
	at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185)
	at com.lgh.hibernate.entity.Type_$$_jvst5e0_0.getName(Type_$$_jvst5e0_0.java)
	at com.lgh.hibernate.test02.TypeDao.main(TypeDao.java:13)

这个异常是什么原因呢??还是因为load的延迟加载机制,当我们通过load()方法来加载一个对象时,此时并没有发出sql语句去从数据库中查询出该对象,当前这个对象仅仅是一个只有id的代理对象,我们还并没有使用该对象,但是此时我们的session已经关闭了,所以当我们在测试用例中使用该对象时就会报LazyInitializationException这个异常了。

所以以后我们只要看到控制台报LazyInitializationException这种异常,就知道是使用了load的方式延迟加载一个对象了,解决这个的方法有三种,一种是将load改成get的方式来得到该对象,另一种是在表示层来开启我们的session和关闭session,还有一种就是。

在dao层load方法后面加上:

Hibernate.initialize(t);

public Type getType() {
		Session session = null;
		try {
			session = HibernateUtil.getSession();
			Type t = (Type) session.load(Type.class, 11);
			// 强制初始化 发送SQL语句向数据库拿数据
			Hibernate.initialize(t);
			return t;
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (session != null) {
				session.close();
			}
		}
		return null;
	}

Load延迟加载的管理

通过上面我们知道使用load方法会产生延迟加载现象那怎么配置让load方法跟get方法一样呢

这有多种方式:

在配置实体类的映射文件添加lazy属性

lazy 属性默认为true 当我们不配置或为true时 ,通过load方法就会有延迟加载,当lazy = “false”时 load方法就会与get方法没什么区别了

<hibernate-mapping>
    <class name="com.lgh.hibernate.entity.Type" table="T_TYPE" lazy="false">
   .....  
 </class>
    <!-- 我们编写的HQL语句 -->
    <query name="getAll">
    from Type where id > ?
    </query>
</hibernate-mapping>

	Type t = (Type) session.load(Type.class, 11);
			System.out.println("load加载 时 类型 "+t.getClass());

Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
load加载 时 类型 class com.lgh.hibernate.entity.Type

我们发现 我们没有访问Type的其他属性 但是当调用load方法时 发出了查询语句 并且此时返回值的类型不再是代理子类 而是type类本身

看到了上面的代码,我们注意到 load 查找时返回返回值的类型是代理子类,其是要继承Type类的才可以产生代理子类 那么当Type类为final 时 ,不能被继承load方法是否还会延迟加载呢?

第二种方法 把实体类 final

                        Type t = (Type) session.load(Type.class, 11);
			System.out.println("load加载 时 类型 "+t.getClass());

Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
load加载 时 类型 class com.lgh.hibernate.entity.Type

我们看到把类修改成不能被继承 此时延迟加载 也不能发挥作用了

关联对象的延迟加载

一对多关联

Type 与Product 时 一对多关系

当在一对多关联映射模式下

 fetch="select"

<!-- class="com.lgh.hibernate.entity.Type" fetch="join" -->
       <!--  fetch 控制如何查关联对象,
       join   采用外连接去拿关联对象 没有产生延迟加载行为
        select 另外发送查询语句查询
        lazy : 三种 值:
            - false  
            - proxy
            - no-proxy
        
       -->
        <many-to-one name="type" cascade="save-update"  fetch="select" >
            <column name="tid" />
        </many-to-one>

当没有配置lazy 时

我们同过 get方法:

session = HibernateUtil.getSession();
            Product p = (Product) session.get(Product.class, 26);
            System.out.println("-------------");
            System.out.println(p.getType().getName());

结果:

Hibernate: select product0_.ID as ID1_3_0_, product0_.NAME as NAME2_3_0_, product0_.PATH as PATH3_3_0_, product0_.DESCRI as DESCRI4_3_0_, product0_.PRICE as PRICE5_3_0_, product0_.tid as tid6_3_0_ from T_PRODUCT product0_ where product0_.ID=?
-------------
Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
水果

我们发现 在 需要用到type 时 才会向数据库查询 在查询关联对象时发生延迟加载

当使用 load方法时:

session = HibernateUtil.getSession();
            Product p = (Product) session.load(Product.class, 26);
            System.out.println("---------------");
            System.out.println(p.getName());
            System.out.println("-------------");
            System.out.println(p.getType().getName());;


---------------
Hibernate: select product0_.ID as ID1_3_0_, product0_.NAME as NAME2_3_0_, product0_.PATH as PATH3_3_0_, product0_.DESCRI as DESCRI4_3_0_, product0_.PRICE as PRICE5_3_0_, product0_.tid as tid6_3_0_ from T_PRODUCT product0_ where product0_.ID=?
西瓜
-------------
Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
水果
我们发现 使用product对象其他属性时向数据库发送查询语句  
 在 需要用到 type 时 才会向数据库查询 在查询关联对象时发生延迟加载

当我们配置

lazy="false"  fetch="select"

<!-- class="com.lgh.hibernate.entity.Type" fetch="join" -->
       <!--  fetch 控制如何查关联对象,
       join   采用外连接去拿关联对象 没有产生延迟加载行为
        select 另外发送查询语句查询
        lazy : 三种 值:
            - false  
            - proxy
            - no-proxy
        
       -->
        <many-to-one name="type" cascade="save-update" lazy="false"  fetch="select" >
            <column name="tid" />
        </many-to-one>

load方法 :

session = HibernateUtil.getSession();
            Product p = (Product) session.load(Product.class, 26);
            System.out.println("---------------");
            System.out.println(p.getName());
            System.out.println("-------------");
            System.out.println(p.getType().getName());


---------------
Hibernate: select product0_.ID as ID1_3_0_, product0_.NAME as NAME2_3_0_, product0_.PATH as PATH3_3_0_, product0_.DESCRI as DESCRI4_3_0_, product0_.PRICE as PRICE5_3_0_, product0_.tid as tid6_3_0_ from T_PRODUCT product0_ where product0_.ID=?
Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
西瓜
-------------
水果

我们 发现 当使用product对象其他属性时向数据库发送查询语句  同时页发送关联对象的查询语句

get方法:

session = HibernateUtil.getSession();
            Product p = (Product) session.get(Product.class, 26);
            System.out.println("---------------");
            System.out.println(p.getName());
            System.out.println("-------------");
            System.out.println(p.getType().getName());
Hibernate: select product0_.ID as ID1_3_0_, product0_.NAME as NAME2_3_0_, product0_.PATH as PATH3_3_0_, product0_.DESCRI as DESCRI4_3_0_, product0_.PRICE as PRICE5_3_0_, product0_.tid as tid6_3_0_ from T_PRODUCT product0_ where product0_.ID=?
Hibernate: select type0_.ID as ID1_6_0_, type0_.NAME as NAME2_6_0_ from T_TYPE type0_ where type0_.ID=?
---------------
西瓜
-------------
水果

在向数据库查询时同时也发送了查询关联对象的SQL语句

多对多关联的延迟加载

teacher 和 student 属于多对多关联

当 lazy="true"  时 会延迟加载,当为false时

  <set name="teachers" table="TEACHER_STUDENT" inverse="false" lazy="false" cascade="save-update" >
            <key column="SID" />
        <!-- 
    lazy的值:
    - true
    - false
    - extra
         -->
          <many-to-many column="TID" class="com.lgh.hibernate.entity.Teacher"></many-to-many>
        </set>
    session = HibernateUtil.getSession();
            Student s = (Student) session.get(Student.class, 1);
            System.out.println("---------------");
            System.out.println(s.getName());
            System.out.println("-------------");
  Hibernate: select student0_.ID as ID1_4_0_, student0_.NAME as NAME2_4_0_ from T_STUDENT student0_ where student0_.ID=?
Hibernate: select teachers0_.SID as SID2_4_0_, teachers0_.TID as TID1_2_0_, teacher1_.ID as ID1_5_1_, teacher1_.NAME as NAME2_5_1_ from TEACHER_STUDENT teachers0_ inner join T_TEACHER teacher1_ on teachers0_.TID=teacher1_.ID where teachers0_.SID=?
Hibernate: select students0_.tid as tid1_5_0_, students0_.sid as sid2_2_0_, student1_.ID as ID1_4_1_, student1_.NAME as NAME2_4_1_ from TEACHER_STUDENT students0_ inner join T_STUDENT student1_ on students0_.sid=student1_.ID where students0_.tid=?
Hibernate: select students0_.tid as tid1_5_0_, students0_.sid as sid2_2_0_, student1_.ID as ID1_4_1_, student1_.NAME as NAME2_4_1_ from TEACHER_STUDENT students0_ inner join T_STUDENT student1_ on students0_.sid=student1_.ID where students0_.tid=?
Hibernate: select teachers0_.SID as SID2_4_0_, teachers0_.TID as TID1_2_0_, teacher1_.ID as ID1_5_1_, teacher1_.NAME as NAME2_5_1_ from TEACHER_STUDENT teachers0_ inner join T_TEACHER teacher1_ on teachers0_.TID=teacher1_.ID where teachers0_.SID=?
---------------
张华
-------------
延迟加载已经没有了

一对一的延迟加载

一对一与一对多相同 这里就不再讲述了

  <!-- 
        lazy : 三种 值:
        	- false  
	        - proxy
	        - no-proxy
        
       -->
        <one-to-one  name="person" class="com.lgh.hibernate.entity.Person" cascade="save-update" ></one-to-one>

hibernate的延迟加载就讲到这里 ,有什么错误的地方还请大家指出来

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值