什么是N+1查询?

问题描述:

什么是N+1查询?
1个回答 分类:综合 2014-09-29

问题解答:

我来补答
默认情况下,当Hibernate从数据库中加载Customer对象时,会同时加载所有关联的Order对象.以Customer和Order类为例,假定ORDERS表的CUSTOMER_ID外键允许为null,图1列出了CUSTOMERS表和ORDERS表中的记录.以下Session的find()方法用于到数据库中检索所有的Customer对象:
List customerLists=session.find("from Customer as c");
运行以上find()方法时,Hibernate将先查询CUSTOMERS表中所有的记录,然后根据每条记录的ID,到ORDERS表中查询有参照关系的记录,Hibernate将依次执行以下select语句:
select * from CUSTOMERS;
select * from ORDERS where CUSTOMER_ID=1;
select * from ORDERS where CUSTOMER_ID=2;
select * from ORDERS where CUSTOMER_ID=3;
select * from ORDERS where CUSTOMER_ID=4;
通过以上5条select语句,Hibernate最后加载了4个Customer对象和5个Order对象,在内存中形成了一幅关联的对象图,参见图2.Hibernate在检索与Customer关联的Order对象时,使用了默认的立即检索策略.这种检索策略存在两大不足:
(1) select语句的数目太多,需要频繁的访问数据库,会影响检索性能.如果需要查询n个Customer对象,那么必须执行n+1次select查询语句.这就是经典的n+1次select查询问题.这种检索策略没有利用SQL的连接查询功能,例如以上5条select语句完全可以通过以下1条select语句来完成:
select * from CUSTOMERS left outer join ORDERS
on CUSTOMERS.ID=ORDERS.CUSTOMER_ID
以上select语句使用了SQL的左外连接查询功能,能够在一条select语句中查询出CUSTOMERS表的所有记录,以及匹配的ORDERS表的记录.
(2)在应用逻辑只需要访问Customer对象,而不需要访问Order对象的场合,加载Order对象完全是多余的操作,这些多余的Order对象白白浪费了许多内存空间.
为了解决以上问题,Hibernate提供了其他两种检索策略:延迟检索策略和迫切左外连接检索策略.延迟检索策略能避免多余加载应用程序不需要访问的关联对象,迫切左外连接检索策略则充分利用了SQL的外连接查询功能,能够减少select语句的数目.附一篇 hibernate的解决n+1的方法 Hibernate的检索策略包括类级别检索策略和关联级别检索策略.
类级别检索策略有立即检索和延迟检索,默认的检索策略是立即检索.在Hibernate映射文件中,通过在上配置 lazy属性来确定检索策略.对于Session的检索方式,类级别检索策略仅适用于load方法;也就说,对于get、qurey检索,持久化对象都会被立即加载而不管lazy是false还是true.一般来说,我们检索对象就是要访问它,因此立即检索是通常的选择.由于load方法在检索不到对象时会抛出异常(立即检索的情况下),因此我个人并不建议使用load检索;而由于中的lazy属性还影响到多对一及一对一的检索策略,因此使用load方法就更没必要了.关联级别检索策略有立即检索、延迟检索和迫切左外连接检索.对于关联级别检索,又可分为一对多和多对多、多对一和一对一两种情况讨论.1)立即检索:这是一对多默认的检索策略,此时lazy=false,outer-join=false.尽管这是默认的检索策略,但如果关联的集合是无用的,那么就不要使用这种检索方式.2)延迟检索:此时lazy=true,outer-join=false(outer-join=true是无意义的),这是优先考虑的检索方式.3)迫切左外连接检索:此时 lazy=false,outer-join=true,这种检索策略只适用于依靠id检索方式(load、get),而不适用于query的集合检索(它会采用立即检索策略).相比于立即检索,这种检索策略减少了一条sql语句,但在Hibernate中,只能有一个配置成 outer-join=true.多对一和一对一检索策略一般使用、配置.中需要配置的属性是 outer-join,同时还需要配置one端关联的的lazy属性(配置的可不是中的lazy哦),它们的组合后的检索策略如下:1) outer-join=auto:这是默认值,如果lazy=true为延迟检索,如果lazy=false为迫切左外连接检索 2) outer-join=true,无关于lazy,都为迫切左外连接检索.3) outer-join=false,如果lazy=true为延迟检索,否则为立即检索.可以看到,在默认的情况下(outer-join=auto,lazy=false),对关联的one端对象Hibernate采用的迫切左外连接检索.依我看,很多情况下,我们并不需要加载one端关联的对象(很可能我们需要的仅仅是关联对象的id);另外,如果关联对象也采用了迫切左外连接检索,就会出现select语句中有多个外连接表,如果个数多的话会影响检索性能,这也是为什么Hibernate通过 hibernate.max_fetch_depth属性来控制外连接的深度.对于迫切左外连接检索,query的集合检索并不适用,它会采用立即检索策略.对于检索策略,需要根据实际情况进行选择.对于立即检索和延迟检索,它们的优点在于select语句简单(每张表一条语句)、查询速度快,缺点在于关联表时需要多条select语句,增加了访问数据库的频率.因此在选择即检索和延迟检索时,可以考虑使用批量检索策略来减少select语句的数量(配置batch-size属性).对于切左外连接检索,优点在于select较少,但缺点是select语句的复杂度提高,多表之间的关联会是很耗时的操作.另外,配置文件是死的,但程序是活的,可以根据需要在程序里显示的指定检索策略(可能经常需要在程序中显示指定迫切左外连接检索).
 
 
展开全文阅读
剩余:2000
上一页:牛刀小试4
下一页:第二问不会作