什么是延迟加载?

延迟加载是指当应用程序想要从数据库获取对象时(在没有设置lazy属性值为false),Hibernate只是从数据库获取符合条件的对象的OId从而生成代理对象,并没有加载出对象

访问该对象的属性时才会加载出相应的值。简答来说就是尽可能的减少查询的数据量。

如何配置延迟加载

在Hibernate中通过.hbm配置文件中的lazy属性来陪值,并且lazy属性出现的位置不同其作用和取值也不同。下面来详细介绍其在不同位置的不同取值和作用

类Class标签中的lazy:

在类标签Class中出现的lazy取值有true(默认值 延迟加载)、false(立即加载)两种取值如下所示:

如果lazy取值为false则表示应用程序从数据库获取对象时会立即加载所有属性值(不包含自定义类型属性)

以员工表为例测试用例如下:

员工表表结构:

员工实体类:

员工表的hbm映射文件:

测试代码:

测试结果:

从上图的结果中我们可以看到emp对象并没有进行延时加载但是其保存部门(Dept)对象的引用的属性并没有进行加载。

我们再来看看当lazy属性的值为true时的结果

从上图我们可以看到lazy的属性不管是True还是false其结果都是一样的!这是为什么呢?Lazy属性的值为true是不是应该延时加载吗?

注意:我们再上面编写测试用例时获取员工对象是用的get方法而我也在之前的博客中说过session对象的get方法不支持延时加载他会忽略掉类级别的lazy属性!我们把get方法换成load方法再来测试。

从上图中我们可以看到当lazy属性值为true时Hibernate并不会一次性加载出所有属性值,只有当程序需要时才去加载从而减少了和数据库交互的负担,提升了程序的性能,这也是延迟加载出现的目的!

多对一关联中的lazy               

如果想要在获取对象的同时立即加载与之关联的自定义类型属性就需要在其多对一配置中设置lazy属性,在此处lazy属性的取值为:proxy:延迟加载(默认值)、no-proxy:无代理延迟加载,false:立即加载

比如我们在实例一的基础上添加lazy属性值为false再来测试:

Set元素中的lazy属性

我们知道如果对象中存在其他实体的集合则需要在hbm文件中配置set元素来进行表间的映射,而在set元素中也可以添加lazy属性其取值为:true:延迟加载(默认值)、false:立即加载、extra:加强延时加载。

在这里不再对false的取值进行测试主要来测试true和extra的区别。我们再实例一的基础上引入Dept(部门)类来进行测试:

部门实体类

部门表映射文件:

测试代码:

lazy属性值为true:

Lazy属性值为extra:

有延时加载而引发的no Session问题

当我们在编写基于分层的B/s程序时常常会因为Session提前关闭而数据没有加载完成而引发no Session的异常如图所示:

该异常引发的原因时同城操作数据的代码编写在DAO层和Biz层但是这两层并不负责数据的展示而我们在jsp页面中对数据进行展示时Session早已关闭并且有与延迟加载的关系数据并没有加载到对象中,当jsp页面去访问对象属性时Hibernate尝试使用Session对象去和数据库交互时发现并没有可用的Session对象从而引发该异常。

对于此类问题同城的做法是利用过滤器(Filter)将Session对象存放在表示层。如下代码所示创建过滤器:

然后在web.xml文件中配置过滤器:

经过以上操作就可以解决no Session的问题。当然还有其他的解决方案,但这种方案是使用最多的也是较为完善的解决方案,

缓存      

缓存的定义

缓存是为了减少应用程序和数据库交互次数而将一些修改频率较低、查询频繁的非关键性数据单独开辟一块空间存放起来的一块空间!是以一定范围内的空间换取用户从数据库查询数据的速度和性能的一种解决方案!

通常缓存分为以下几类:

内部缓存、二级缓存、查询缓存以及第三方缓存实现。

内部缓存

在Hibernate中内部缓存又称为一级缓存和事务级缓存由Hibernate自动维护不可卸载。其生命周期和Session对象的生命周期相同,当Session关闭时该缓存也会被自动回收。如下所示:

其运行结果如下:

从结果中我们可以发现两次查询数据库时Hibernate值是生成了一条sql语句也就是说只有第一次查询时和数据库进行了交互,并将查询出的对象放入了内部缓存当第二次查询时Hibernate发现内部缓存中已经存在该对象则直接将该对象返回不在和数据库进行交互,并且这两次查询的对象的内存地址是完全相同的,由此可以得出内部缓存中缓存的是对象的内存地址的引用而不是对象的各个属性值!

二级缓存

二级缓存是可配置的插件,是进程或集群范围内的缓存,可以被所有的Session共享

二级缓存的配置

在Hibernate中配置二级缓存的插件有很多下面使用EHCache插件为例来配置二级缓存。

1.引入如下jar包。

      ehcache-1.2.3.jar  核心库

      backport-util-concurrent.jar

      commons-logging.jar

   2.配置Hibernate.cfg.xml开启二级缓存

<propertyname="hibernate.cache.use_second_level_cache">true</property>

3.配置二级缓存的供应商

<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

注:property元素必须在mapping元素之上

4.配置可进入二级缓存的类

<class-cache usage="read-write" class="cn.happy.entity.Emp"/>

5.在Classpath目录下引入ehcache.xml文件

经过以上5个步骤就可以将Dept对象放入二级缓存了,下面编写测试用例

测试结果:

从结果中我们可以开出第二次查询部门时并没有生成生成sql语句但是我们两次打印出的对象却不是同一个内存地址,这是因为二级缓存中存放的并不是对象象的内存地址的引用而是对象的散装属性(可以看成是对象的各个属性值)所以我们访问二级换存时需要将这些散装属性重新再内存中拼装成一个完整的对象。如果你仔细看的话你会发现当我们第二次去访问员工集合时Hibernate还是会生成sql语句。那是因为以上二级缓存的配置是针对类针对类级别的。如果想要将员工集合也进行缓存的话就需要在hibernate.cfg.xml配置文件中加入集合缓存的配置,内容如下:

注:该配置必须是mapping元素的下一个元素

这时我们再来运行测试用来结果如下:

看到结果后你可能会大吃一惊,我没配集合缓存时他只生成一条sql语句我陪完之后怎么变成两条sql了?但是你注意看着两条sql语句是一模一样的,都是根据员工对象的OID来进行查询的!那么员工对象的OID是从哪来的呢?没错,就是从二级缓存中获取的至于为什么只是缓存员工对象的OID而没有缓存其他属性值,是因为员工对象没有配置二级缓存其不能进入二级缓存也就没有办法从二级缓存中拿到它的其他属性值,我们对员工对象配置二级缓存后再来进行测试。

其结果如下:

查询缓存

查询缓存的配置

在hibernate.cfg.xml配置文件中加入上述元素开启二级缓存。并且在使用Query进行缓存时必须在获取集合前调用query1.setCacheable(true);

下面编写测试用例进行测试:

测试结果:

注意:

1.查询缓存是基于二级缓存的在配置查询缓存时必须配置二级缓存否则将抛出如下异常

2.在查询缓存中保存的是对象内存地址的引用而不是对象的散装属性。

3.查询缓存是根据两次HQL查询语句经Hibernate内部转化后生成的sql语句是否一样留在决定是否和数据库进行交互而不是根据其对象的OID如下面的测试用例虽然查询的是OID相同的对象但还是会和数据库进行交互!

测试结果:

延迟加载和缓存遗留问题

Lazy属性和fetch属性连用

在一对多或者多对多检索策略由lazy和fetch共同确定,Lazy:决定关联对象初始化时机,Fetch:决定SQL语句构建形式。Fetch属性的取值为:Join:迫切左外连接、Select:多条简单SQL(默认值)、Subselect:子查询。当Fetch属性取值为join是将忽略lazy属性采用立即加载策略。例如插叙编号为5的部门,即便将部门映射文件中映射员工集合的set元素中的lazy属性设置为true或extra其还是会立即加载出该部门下的所有员工的集合,其Hibernate内部生成的sql语句如下:

Query接口的list方法和iterate方法的区别

1.返回的类型不一样,list返回List,iterate返回Iterator,
2.获取数据的方式不一样,list会直接查数据库,iterate会先到数据库中把id都取出来,然后真正要遍历某个对象的时候先到缓存中找,如果找不到,以id为条件再发一条sql到数据库,这样如果缓存中没有数据,则查询数据库的次数为n+1。
3.iterate会查询2级缓存,list 只会缓存,但不会使用缓存(除非结合查询缓存)。
4.list中返回的List中每个对象都是原本的对象,iterate中返回的对象是代理对象

缓存的内部存储实现

1.在缓存中都是以map集合的形式对象数据

2.一级缓存和二级缓存中都是以对象的OID作为map结合的key值而查询缓存是以Hibernate内部生成的sql语句作为key值

3.一级缓存和查询缓存中map集合的value值存放的是内存对象的引用,而二级缓存中存放的是对象的散装属性。

4.当应用程序需要从数据库获取数据时Hibernate会以此检索一级缓存或查询缓存>二级缓存或查询缓存中是否有符合条件的数据如果有则直接返回不再进行和数据库的交互,反之则生成sql语句去和数据库进行交互获取相应的数据再进行返回并依次将给数据放入一级缓存>二级缓存或查询缓存

将二级缓存保存到硬盘

在classpath目录下的ehcache.xml配置文件中的diskStore元素的path属性值设置为想要保存的路径并且将cache元素中的maxElementsInMemory属性值设置为0。

Hibernate中延迟加载和缓存的更多相关文章

  1. Hibernate-ORM:16.Hibernate中的二级缓存Ehcache的配置

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 本篇博客讲述Hibernate中的二级缓存的配置,作者将使用的是ehcache缓存 一,目录 1.二级缓存的具 ...

  2. 在Spring、Hibernate中使用Ehcache缓存(2)

    这里将介绍在Hibernate中使用查询缓存.一级缓存.二级缓存,整合Spring在HibernateTemplate中使用查询缓存.,这里是hibernate3,使用hibernate4类似,不过不 ...

  3. 具体解释Hibernate中的二级缓存

    1.前言 这篇博客再前几篇博客的基础上来解说一下.Hibernate中的二级缓存.二级缓存是属于SessionFactory级别的缓存机制. 第一级别的缓存是Session级别的缓存,是属于事务范围的 ...

  4. hibernate中的一级缓存与闪照区

    首先Hibernate中的一级缓存默认是打开的,并且范围从session创建到session关闭,存储的数据必须是持久态的数据. 1 //从session创建开始,一级缓存也跟着创建 2 Sessio ...

  5. Hibernate中的一级缓存、二级缓存和懒加载(转)

    1.为什么使用缓存 hibernate使用缓存减少对数据库的访问次数,从而提升hibernate的执行效率.hibernate中有两种类型的缓存:一级缓存和二级缓存. 2.一级缓存 Hibenate中 ...

  6. Hibernate中的一级缓存、二级缓存和懒加载

    1.为什么使用缓存 hibernate使用缓存减少对数据库的访问次数,从而提升hibernate的执行效率.hibernate中有两种类型的缓存:一级缓存和二级缓存. 2.一级缓存 Hibenate中 ...

  7. Hibernate中 一 二级缓存及查询缓存(2)

    缓存:缓存是什么,解决什么问题?  位于速度相差较大的两种硬件/软件之间的,用于协调两者数据传输速度差异的结构,均可称之为缓存Cache.缓存目的:让数据更接近于应用程序,协调速度不匹配,使访问速度更 ...

  8. Hibernate中 一 二级缓存及查询缓存(1)

    最近趁有空学习了一下Hibernate的缓存,其包括一级缓存,二级缓存和查询缓存(有些是参照网络资源的): 一.一级缓存     一级缓存的生命周期和session的生命周期一致,当前sessioin ...

  9. hibernate中的session缓存

    1.什么是session缓存? 在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存. 只要 Session 实例没有结束生命周期, 且没有 ...

随机推荐

  1. IOS APP 国际化 程序内切换语言实现 不重新启动系统(支持项目中stroyboard 、xib 混用。完美解决方案)

    上篇 IOS APP 国际化(实现不跟随系统语言,不用重启应用,代码切换stroyboard ,xib ,图片,其他资源 介绍了纯代码刷新 实现程序内切换语言. 但效率底下,也存在一些问题.暂放弃. ...

  2. MySQL执行计划中key_len详解

    (1).索引字段的附加信息:可以分为变长和定长数据类型讨论,当索引字段为定长数据类型,比如char,int,datetime,需要有是否为空的标记,这个标记需要占用1个字节:对于变长数据类型,比如:v ...

  3. python字符串格式化方法 format函数的使用

      python从2.6开始支持format,新的更加容易读懂的字符串格式化方法, 从原来的% 模式变成新的可读性更强的 花括号声明{}.用于渲染前的参数引用声明, 花括号里可以用数字代表引用参数的序 ...

  4. [js] js判断浏览器(转)

    (function($, window, document,undefined){ if(!window.browser){ var userAgent = navigator.userAgent.t ...

  5. Quartz 2D绘制简单图形

    在Quartz 2D中,绘图是通过图形上下文进行绘制的,以下绘制几个简单的图形 首先先创建一个QuartzView.swift文件继承自UIView,然后实现drawRect方法: import UI ...

  6. MySQL数据库从GBK转换到UTF-8最简单解决方案(也适用于其它编码转换)

    1.使用mysqldump导出表结构,如: mysqldump -d -u root -p 数据库名 >/root/struct.sql 2.使用mysqldump以特定编码导出数据(其中utf ...

  7. 为Eclipse添加Java和Android SDK源代码

    1.添加jdk源码进入eclipse Ctrl + Click -->Attached Source 路径:D:\Program Files\Java\jdk1.8.0_45\src.zip 2 ...

  8. 用手机地图GPS导航费流量吗?

    如果你的手机带有GPS芯片,那么使用手机导航是不会耗费手机流量的.但是如果你的手机没有GPS芯片,而使用的导航软件又是类似于移动提供的导航服务那样的导航功能,那就耗费手机流量了. 目前,导航软件导航主 ...

  9. sys.stdout sys.stderr的用法

    stdout:标准输出 stderr:标准错误 print  相当于 sys.stdout.write() + 换行 一个将数据流写入文件的程序,文件名为:main.py def main(out=s ...

  10. WIN8 下Cisco VPN连接 出现vpn 422 failed to enable virtual adapter错误

    今天在家用VPN软件连接,出现了“vpn 422 failed to enable virtual adapter”的错误,系统安装的是Win8专业版32位,百度了半天又很多方法解决不了,后来发现了一 ...