hibernate系列十二:延迟加载
来源:互联网 发布:鼻子丑怎么办 知乎 编辑:程序博客网 时间:2024/04/19 22:57
一. 简介
当Hibernate从数据库中加载DepartEntity对象时,如果同时自动加载所有关联的StudentEntity对象,而程序实际上仅仅需要访问DepartEntity对象,那么这些关联的StudentEntity对象就白白浪费了许多内存空间。本节以DepartEntity和StudentEntity类为例,介绍如何设置延迟加载,以优化查询性能。
Hibernate查询DepartEntity对象时,立即查询并加载与之关联的StudentEntity对象,这种查询策略称为立即加载。立即加载存在两大不足:
(1) select语句的数目太多,需要频繁地访问数据库,会影响查询性能。
(2)在应用程序只需要访问DepartEntity对象,而不需要访问StudentEntity对象的场合,加载StudentEntity对象完全是多余的操作,这些多余的StudentEntity对象白白浪费了许多内存空间。
为了解决以上问题,Hibernate提供了延迟加载策略。延迟加载策略能避免加载应用程序不需要访问的关联对象。
Hibernate允许在对象一关系映射文件中配置加载策略。表1列出了用于设置加载策略的lazy属性。
类级别<class>元素中Iazy属性的可选值为: 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二. 类级别的查询策略
类级别可选的加载策略包括立即加载和延迟加载,默认为延迟加载。如果<class>元素的lazy属性为true,表示采用延迟加载;如果lazy属性为false,表示采用立即加载。下面以DepartEntity.hbm.xml为例进行说明。
1. 立即加载
在DepartEntity.hbm.xml文件中,以下方式表示采用立即加载策略:
<classname="com.hopetech.entity.DepartEntity" table="Depart" lazy="false">
当通过Session的load()方法加载DepartEntity对象时:
DepartEntity dept=(DepartEntity)session.load(DepartEntity.calss,121);
Hibernate会立即执行查询Depart表的select语句:
select * from Depart where deptId=?
2.延迟加载
类级别的默认加载策略是延迟加载。在DepartEntity.hbm.xml文件中,以下两种方式都表示采用延迟加载策略:
<classname="com.hopetech.entity.DepartEntity" table="Depart" lazy="true">
<classname="com.hopetech.entity.DepartEntity" table="Depart">
如果程序加载一个持久化对象的目的是为了访问它的属性,则可以采用立即加载。如果程序加载一个持久化对象的目的仅仅是为了获得它的引用,则可以采用延迟加载,无须访问DepartEntity对象的属性,如示例1所示。
示例l
DepartEntity dept=(DepartEntity)session.load(DepartEntity.calss,121);
StudentEntity student=new StudentEntity(1101, "Tom");
student.setDept(dept);
session.save(student);
示例l6向数据库保存了一个StudentEntity对象,它与已经存在的一个DepartEntity持久化对象关联。如果在DepartEntity类级别采用延迟加载,则session.load()方法不会执行访问Depart表的select语句,只返回一个DepartEntity代理类的对象,它的departId属性为121,其余属性都为NULL。session.save()方法执行的SQL insert语句:
insertinto Student(stuId,stuName,…,deptId) values(1101,’Tom’,…,121)
当<class>元素的1azy属性为true时,会影响Session的load()方法的各种运行时行为,下面举例说明。
(1)如果加载的DepartEntity对象在数据库中不存在,Session的load()方法不会抛出异常,只有当运行dept.getDepartName()方法时才会抛出以下异常:
org.hibernate.ObjectNotFoundException:Norow with the given identifier exists
(2)如果在整个Session范围内,应用程序没有访问过DepartEntity对象,那么DepartEntity代理类的实例一直不会被初始化,Hibernate不会执行任何select语句。以下代码试图在关闭Session后访问DepartEntity游离对象:
DepartEntity dept=(DepartEntity)session.load(DepartEntity.calss,121);
session.close();
dept.getDeptName();
由于变量dept引用的DepartEntity代理类的实例在Session范围内始终没有被初始化,因此执行dept.getDeptName()方法时,Hibernate会抛出以下异常:
org.hibernate.LazyInitializationException:couldnot initialize proxy-no Session
由此可见,DepartEntity代理类的实例只有在当前Session范围内才能被初始化。
(3)org.hibernate.Hibernate类的initialize()静态方法用于在Session范围内显式初始化代理类实islnitialized()方法用于判断代理类实例是否已经被初始化。例如:
DepartEntity dept=(DepartEntity)session.load(DepartEntity.calss,121);
if(!Hibernate.isInitialized(dept))
Hibernate.initialize(dept);
session.close();
dept.getDeptName();
以上代码在Session范围内通过Hibernate类的initialize()方法显式初始化了DepartEntity代理类实例,因此Session关闭后,可以正常访问DepartEntity游离对象。
(4)当应用程序访问代理类实例的getDeptId()方法时,不会触发Hibernate初始化代理类实例的行为,例如:
DepartEntity dept=(DepartEntity)session.load(DepartEntity.calss,121);
dept.getDeptId();
session.close();
dept.getDeptName();
当应用程序访问dept.getDeptId()方法时,该方法直接返回DepartEntity代理类实例的OID值,无须查询数据库。由于变量dept始终引用的是没有被初始化的DepartEntity代理类实例,因此当Session关闭后再执行dept.getDeptName()方法,Hibernate会抛出以下异常:
org.hibernate.LazyInitializationException:couldnot initialize proxy-no Session
值得注意的是,不管DepartEntity.hbm.xml文件的<class>元素的Lazy属性是true还是false,Session的get()方法及Query对象的list()方法在DepartEntity类级别总是使用立即加载策略,举例说明如下:
(1) Session的get()方法总是立即到数据库中查询DepartEntity对象,如果在数据库中不存在相应的数据,就返回NULL。例如:
DepartEntity dept=(DepartEntity)session.get(DepartEntity.calss,121);
当通过Session的get()方法加载DepartEntity对象时,Hibernate会立即执行以下select语句:
select * from Depart where deptId=?
如果存在相关的数据,get()方法就返回DepartEntity对象,否则就返回NULL。get()方法永远不会返回DepartEntity代理类实例,这是与load()方法的又一个不同之处。
(2) Query对象的list()方法总是立即到数据库中查询DepartEntity对象,例如:
List deptList=session.createQuery(“from DepartEntity”).list();
当运行Query对象的list()方法时,Hibernate立即执行以下select语句:
select * from Depart
三 一对多关联的查询策略
推荐默认按照延迟加载策略进行。
在映射文件中,用<set>元素来配置一对多关联及多对多关联关系的加载策略。DepartEntity.hbm.xml文件中的以下代码用于配置DepartEntity和StudentEntity类的一对多关联关系:
<set name="students" inverse="true" lazy="true">
<key column="deptId"></key>
<one-to-many class="com.hopetech.entity.StudentEntity"></one-to-many>
</set>
<set>元素有lazy属性,主要决定students集合被初始化的时机,即到底是在加载DepartEntity对象时就被初始化,还是在程序访问students集合时被初始化。表2描述了<set>元素的lazy属性取不同值时设置的查询策略。
lazy属性
(默认值为true)加载策略
true延迟加载false立即加载extra加强延迟加载1.立即加载
以下代码表明DepartEntity类的students集合采用立即加载策略:
<set name="students" inverse="true" lazy="false">…</set>
示例17通过Session的get()方法加载OID为121的DepartEntity对象。
示例17
DepartEntity dept=(DepartEntity)session.get(DepartEntity.calss,121);
执行Session的get()方法时,对于DepartEntity对象采用类级别的立即加载策略;对于DepartEntity对象的Students集合(即与DepartEntity关联的所有StudentEntity对象),采用一对多关联级别的立即加载策略。因此Hibernate执行以下select语句:
select * from Depart where dept0_.deptId=?
select * from Student where deptId=?
通过以上select语句,Hibernate加载了一个DepartEntity对象和多个StudentEntity对象。但在很多情况下,应用程序并不需要访问这些StudentEntity对象,加载StudentEntity对象显然是多余的,所以在一对多关联级别中不能随意使用立即加载策略。
2.延迟加载
对于<set>元素,应该优先考虑使用默认的延迟加载策略:
<set name="students" inverse="true">…</set>
或者
<set name="students" inverse="true" lazy="true">…</set>
运行示例17中Session的get(DepartEntity.class,121)方法时,仅立即加载DepartEntity对象,执行以下select语句:
select * from Depart where dept0_.deptId=?
Session的get()方法返回的DepartEntity对象的students属性引用一个没有被初始化的集合代理类实例,换句话说,此时students集合中没有存放任何StudentEntity对象。只有当students集合代理类实例被初始化时,才会到数据库中查询所有与DepartEntity关联的所有StudentEntity对象,执行以下select语句:
select * from Student where deptId=?
那么,DepartEntity对象的students属性引用的集合代理类实例何时初始化呢?主要包括以下两种情况:
(1)当应用程序第一次访问它时,如调用它的iterator()、size()、isEmpty()或contains()方法:
Set<StudentEntity> students=dept.getStudents();
//以下语句导致students集合代理类实例被初始化
Iterator<StudentEntity> stuIterator= students.iterator();
(2)通过org.hibernate.Hibernate类的initialize()静态方法初始化它:
Set<StudentEntity> students=dept.getStudents();
//以下语句导致students集合代理类实例被初始化
Hibernate.initialize(students);
3.增强延迟加载
在<seT元素中配置lazy属性为“extra”。以下代码表明采用增强延迟加载策略:
<set name="students" inverse="true" lazy="extra">…</set>
增强延迟加载策略与一般的延迟加载策略(lazy=”true”)很相似。主要区别在于,增强延迟加载策略能进一步延迟DepartEntity对象的students集合代理类实例的初始化时机。当程序第一次访问students属性的iterator()方法时,会导致students集合代理类实例的初始化,而当程序第一次访问students属性的size()、contains()和isEmpty()方法时,Hibernate不会初始化students集合代理实例,仅通过特定的select语句查询必要的信息。以下程序代码演示了采用增强延迟加载策略时的Hibernate运行时行为:
DepartEntity dept=(DepartEntity)session.get(DepartEntity.calss,121);
//以下语句不会初始化students集合代理类实例
//执行SQL语句:select count(stuId) from student where deptId=?
dept.getStudents().size();
//以下语句会初始化students集合代理类实例
//执行SQL语句:select* from student where deptId=?
dept.getStudents.iterator();
在映射文件中,<many-to-one>元素用来设置多对一关联关系。在StudentEntity.hbm.xml文件中,以下代码设置StudentEntity类与DepartEntity类的多对一关联关系。
<many-to-one
name="dept"
column="deptId"
lazy="proxy"
class= "com.hopetech.entity.DepartEntity"/>
<many-to-one>元素的lazy属性取不同值时设置的加载策略,如表3所示。
lazy属性
(默认值为proxy)加载策略proxy延迟加载no-proxy无代理延迟加载False立即加载如果没有显式设置lazy属性,那么在多对一关联级别采用默认的延迟加载策略。假如应用程序仅仅希望访问StudentEntity对象,并不需要立即访问与StudentEntity关联的DepartEntity对象,则应该使用默认的延迟加载策略。
1.延迟加载
在<many-to-one>元素中配置lazy属性为“proxy”,延迟加载与StudentEntity关联的DepartEntity对象,如示例l8所示。
示例18
StudentEntity student=(StudentEntity)sesion.get(StudentEntity.class,1104)
//student.getDept()返回DepartEntity代理类实例的引用
DepartEntity dept=student.getDept();
dept.getDeptName();
当运行Session的get()方法时,仅立即执行查询StudentEntity对象的select语句:
select * from Student where stuId=?
StudentEntity对象的dept属性引用DepartEntity代理类实例,这个代理类实例的OID由Student表的deptId外键值决定。当执行dept.getDeptName()方法时,Hibernate初始化DepartEntity代理类实例,执行以下select语句到数据库中加载DepartEntity对象:
select * from Depart where deptId=?
2.无代理延迟加载
在<many-to-one>元素中配置lazy属性为“no-proxy”。对于以下程序代码:
//第1行
StudentEntitystu dent=(StudentEntity)sesion.get(StudentEntity.class,1104)
DepartEntity dept=student.getDept();//第2行
dept.getDeptName();//第3行
如果对StudentEntity对象的dept属性使用无代理延迟加载,即<many-to-one>元素的lazy属性为no-proxy,那么程序第1行加载的StudentEntity对象的dept属性为NULL。当程序第2行调用student.getDept()方法时,将触发Hibernate执行查询Depart表的select语句,从而加载DepartEntity对象。
由此可见,当lazy属性为proxy时,可以延长延迟加载DepartEntity对象的时间。而当lazy属性为no-proxy时,则可以避免使用由Hibernate提供的DepartEntity代理类实例,使Hibernate对程序提供更加透明的持久化服务。
注意 当lazv属性为no-proxy时,需要在编译时使用工具增强持久化类的字节码,否则运行情况和lazy属性为proxy时相同。增强持久化类的详细配置请查看Hibernate帮助文档。
3.立即加载
在<many-to-one>元素中配置lazy属性为“false”。以下代码把StudentEntity.hbm.xml文件的<many-to-one>元素的lazy属性设为false:
<many-to-one
name="dept"
column="deptId"
lazy="false"
class="com.hopetech.entity.DepartEntity"/>
对于以下程序代码:
StudentEntity student=(StudentEntity)sesion.get(StudentEntity.class,1104)
在运行session.get()方法时,Hibernate执行以下select语句:
select * from Student where stuId=?
select * from Depart where deptId=?
- hibernate系列十二:延迟加载
- Hibernate中的延迟加载
- Hibernate延迟加载机制
- Hibernate延迟加载机制
- 再论hibernate延迟加载
- HIBERNATE延迟加载原理
- Hibernate延迟加载机制
- Hibernate延迟加载
- hibernate延迟加载
- hibernate延迟加载
- Hibernate延迟加载机制
- Hibernate延迟加载机制
- Hibernate延迟加载机制
- Hibernate延迟加载机制
- Hibernate延迟加载
- Hibernate属性延迟加载
- hibernate延迟加载
- HIbernate延迟加载
- 机器人的人工智能与伦理道德
- oracle表示百分数,加减,统计
- Shell(三)
- logback 配置详解(一)
- C语言指针入门(二)
- hibernate系列十二:延迟加载
- 面试新的体会(第一天)
- Boot流程
- php将图片以二进制形式保存到mysql数据库的解决方法
- Qt Creator(mingw)下编译和配置OpenCV
- 【Android】Android 7中logcat读logdr的处理
- 单例模式
- Java Servlet(三) 监听器
- Shell(四)