hibernate延迟加载分析

来源:互联网 发布:网络代刷平台 编辑:程序博客网 时间:2024/05/22 23:26


在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语句,只有当我们需要使用的时候才会从数据库中去查询

请看下面的一段代码:

@Testpublic 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_0Hibernate: 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.Typeload 加载 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语句去从数据库中查询出来

@Testpublic 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.NullPointerExceptionat 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_0Hibernate: 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 Sessionat 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
原创粉丝点击