Hibernate的三种检索策略

1 N+1问题
Hibernate的Session缓存中存放的是相互关联的对象图,默认情况下,当从数据库中加载一个对象的时候,会同时加载它所关联的其他对象。例如:Clazz(班级)跟Student(学生)是1:N关联关系。Clazz对象中有Student对象的集合。具体例子可参见笔者的另一篇文章,《Hibernate初体验》 http://frank1234.iteye.com/blog/2172205

如果要执行语句:
List list = session.createQuery("from Clazz").list();
假设clazz表中有3条记录,id分别等于1、2、3。
那么默认情况下Hibernate会执行如下语句:
select * from clazz;
select * from student where clazzid=1;
select * from student where clazzid=2;
select * from student where clazzid=3;
可见clazz表中有3条记录则执行了3+1条语句,这就是n+1问题,由于用户加载了Clazz 未必想查询班级中的学生,但是却多执行了3条SQL语句,这对性能的影响是巨大的,所以针对这种情况Hibernate 提供了三种不同的检索策略。

2 三种检索策略
1)立即检索:立即加载指定的对象及对应的关联对象,这是Hibernate默认的检索策略,如上n+1问题所述。
适用场景:通常类级别的的检索适合这种策略,因为加载的对象通常都会用到。
2)延迟检索:延迟加载指定的对象及对应的关联对象,如n+1问题中,只执行select * from clazz,而不立即执行后面的3条语句,等真正用到的时候才执行,这就带来了巨大的性能提升。
适用场景:它通常适用于一对多和多对多关联场景,因为关联对象不一定会用到。
3)迫切左外连接检索:通过左外连接加载指定的对象及对应的关联对象。例如对于n+1问题,执行的SQL语句变为:
select * from clazz a left join student b on a.id=b.clazzid; 这样也会某种程度的提升性能。
适用场景:适用于多对一和一对一关联场景,并且关联查询性能较好的场景。
其实如果考虑数据库缓存命中率的话,N+1次select未必比这种关联方式慢。

3 类级别的检索策略
1)立即检索:<class lazy="false">
如1中所述的n+1问题,会立即检索指定的对象及对应的关联对象。

2)延迟检索:<class lazy="true"> 。
Clazz course = (Course)session.load(Clazz.class, 1);
延迟检索会返回一个Clazz类的代理类的实例,Hibernate是通过CGLib字节码增强技术实现的,生成的代理类继承了Clazz类。然后根据load的第二个参数设置了对象的id属性,其他属性的值为空。当执行getxxx()或者setxxx()方法时,才真正的到数据库中检索数据。这个xxx不含id属性,load完后执行getId()仍然不需要走库,因为生成代理类对象的时候id已经赋值了。

Hibernate3.x默认是true

延迟检索仅对load()方法生效, get()等方法总是采用立即检索策略


猜你喜欢

转载自frank1234.iteye.com/blog/2174120