一、延迟加载定义
延迟加载,也叫懒加载,它是Hibernate为提高程序执行效率而提供的一种机制,即当只有真正使用该对象的数据时才会创建。说白了,所谓的延迟加载不是在load的时候去数据库加载数据,而是当我在应用程序中真正使用该数据时Hibernate才去查询、加载相应的数据。
二、延迟加载实现原理
Hibernate中主要是通过代理(proxy)机制来实现延迟加载,即Hibernate丛数据库获取某一个对象数据时、获取某一 个对象的集合属性值时,或获取某一个对象所关联的另一个对象时,由于没有使用该对象的数据,hibernate并不是数据库加载真正的数据,而只是为该对 象创建一个代理对象来代表这个对象,这个对象上的所有属性都是默认值;只有在真正需要使用该对象的数据时才创建这个真实对象,真正从数据库中加载它的数据。
如:
User user=(User)session.load(clazz, id);
System.out.println(user.getId());
user.getName();
执行前两句代码时直接返回的是代理对象,没有发送sql语句到数据库加载。这样生成的代理对象具有与真实对象相同的属性和方法,只是在实例化的时候只实例化了该对象的id,当真正使用该对象的其他属性和方法时(执行第三句代码),才创建真实的User实例,并发送sql语句到数据库中载数据,这样大大节省了内存。
三、延迟加载应用环境
当我们要访问的数据量过大时,明显用缓存不太合适, 因为内存容量有限 ,为了减少并发量,减少系统资源的消耗,这时Hibernate用懒加载机制来弥补这种缺陷,但是这只是弥补而不是用了懒加载总体性能就提高了。
实现懒加载的条件:
1 实体类不能是final的
2 能实现懒加载的对象都是被CGLIB(反射调用)改写的代理对象,所以不能是final修饰的
3 须要asm,cglib两个jar包
4 相应的lazy属性为true
5 相应的fetch属性为select
Hibernate实现的懒加载:
1、 通过Session.load()实现懒加载
load(Object, Serializable):根据id查询 。查询返回的是代理对象,不会立刻访问数据库,是懒加载的。当真正去使用对象的时候才会访问数据库。
使用load()时如果在session关闭之后再查询此对象,会报异常:could not initialize proxy - no Session。处理办法:
a.在session关闭之前初始化一下查询出来的对象:Hibernate.initialize(user);
b.
在web.xml中配置OpenSessionInView过滤器即可解决
<!-- OpenSessionInView 配置,解决Hibernate延迟加载的问题 -->
<filter>
<filter-name>lazyLoadingFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>lazyLoadingFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
2、 one-to-one(元素)实现了懒加载。
在一对一的时候,查询主对象时默认不是懒加载。即:查询主对象的时候也会把从对象查询出来。
需要把主对象配制成lazy="true" constrained="true" fetch="select"。此时查询主对象的时候就不会查询从对象,从而实现了懒加载。
3、 many-to-one(元素)实现了懒加载。
多对一的时候,查询主对象时默认是懒加载。即:查询主对象的时候不会把从对象查询出来。
多对一的时候,查询从对象时默认是懒加载。即:查询从对象的时候不会把主对象查询出来。
hibernate3.0中lazy有三个值,true,false,proxy,默认的是lazy="proxy".具体设置成什么要看你的需求,并不是说哪个设置就是最好的。在<many-to-one>与<one-to-one>标签上:当为true时,会有懒加载特性,当为false时会产生N+1问题,比如一个学生对应一个班级,用一条SQL查出10个学生,当访问学生的班级属性时Hibernate会再产生10条SQL分别查出每个学生对应的班级.
fetch= 捉取方式:select=关联查询;join=连接表的方式查询(效率高),
fetch=join时,lazy的设置将没有意义.
4、 one-to-many(元素)懒加载:默认会懒加载,这是必须的,是重常用的。
一对多的时候,查询主对象时默认是懒加载。即:查询主对象的时候不会把从对象查询出来。
需要配置主对象中的set集合lazy="false" 这样就配置成是不懒加载了。或者配置抓取方式fetch="join"也可以变成不懒加载。
四、总结
根据以上总结我们发现,在Hibernate的延迟加载机制中,大部分是针对集合类型(关联映射设置多的一方延迟加载)的应用意义最为重大,因为这有可能使性能得到大幅度的提高。而Hibernate 的延迟加载(lazy load)本质上就是代理模式的应用,通过代理模式来降低系统的内存开销、提升应用的运行性能。Hibernate 充分利用了代理模式的这种优势,并结合了 Javassist 或 CGLIB 来动态地生成代理对象,这更加增加了代理模式的灵活性,只不过Hibernate 给这种用法一个新名称:延迟加载。无论如何,分析、了解这些开源框架的实现可以更好的体会到这些经典设计模式的优势所在。