[NHibernate]N+1 Select查询问题分析

时间:2022-03-13 12:21:27

目录

写在前面

文档与系列文章

N+1 Select查询问题分析

总结

写在前面

在前面的文章(延迟加载,立即加载)中都提到了N+1 Select的问题,总觉得理解的很不到位,也请大家原谅,这也是为什么单独将该问题拿出来做分析的原因。nhibernate的默认Lazy加载方式是解决N+1 select问题的一种方案,而我自身的理解是立即加载可以解决,完全的背道而驰了。写出那篇文章后,对这个问题,一直念念不忘,总觉得哪地方不对劲。由于我对问题的理解很不透彻,也同样造成你的误解,真的很抱歉。

文档与系列文章

[Nhibernate]体系结构

[NHibernate]ISessionFactory配置

[NHibernate]持久化类(Persistent Classes)

[NHibernate]O/R Mapping基础

[NHibernate]集合类(Collections)映射 

[NHibernate]关联映射

[NHibernate]Parent/Child

[NHibernate]缓存(NHibernate.Caches)

[NHibernate]NHibernate.Tool.hbm2net

[NHibernate]Nullables

[NHibernate]Nhibernate如何映射sqlserver中image字段

[NHibernate]基本配置与测试 

[NHibernate]HQL查询 

[NHibernate]条件查询Criteria Query

[NHibernate]增删改操作

[NHibernate]事务

[NHibernate]并发控制

[NHibernate]组件之依赖对象

[NHibernate]一对多关系(级联删除,级联添加)

[NHibernate]一对多关系(关联查询)

[NHibernate]多对多关系(关联查询)

[NHibernate]延迟加载

[NHibernate]立即加载

[NHibernate]视图处理

N+1 Select查询问题分析

使用sql查询数据的时候,如果select太多,势必也会对数据库造成压力,影响性能。比如如果查询n个Customer对象,则一次查询获得所有的customer对象,然后根据customerid查询关联的Order表。(立即加载的情况下

一个例子

         /// <summary>
/// NHibernateUtil方式,立即加载客户信息及关联的数据
/// </summary>
/// <param name="customerID"></param>
/// <returns></returns>
public Customer GetCustomerByImmediatelyLoadNHibernateUtil(Guid customerID)
{
//获得ISession实例
//通过using的方式将session释放,为了保证是立即加载的数据,而不是延迟加载的
//还记得上篇文章中提到的在延迟加载中,在关闭session情况下,再读取数据的时候就会有一个异常信息
//忘记的可以再回到上一篇文章进行查看
using (ISession session = NHibernateHelper.GetSession())
{
Customer customer = session.Get<Customer>(customerID);
//
// 摘要:
// Force initialization of a proxy or persistent collection.
//
NHibernate.NHibernateUtil.Initialize(customer.Orders);
return customer;
}
}

这里使用NHinernateUtil实用类进行强制立即加载方式,测试代码

         protected void btnImmediately_Click(object sender, EventArgs e)
{
Business.CustomerBusiness customerBusiness = new Business.CustomerBusiness();
Customer customer = customerBusiness.GetCustomerByImmediatelyLoadNHibernateUtil(new Guid("B0720295-9541-40B3-9994-610066224DB8"));
bool isInitOrder = NHibernate.NHibernateUtil.IsInitialized(customer.Orders);
}

查看生成的sql语句

[NHibernate]N+1 Select查询问题分析

本来我们是想查询customer,一次查询就够,可是通过监视到的sql,发现多了一次sql查询,也就是查询customer的关联数据表的数据。

而在延迟加载的情况下,则会在需要的时候才会去查询,性能上面更好。(有关延迟加载的内容,请在系列文章中查找,这里不再赘述了)

如果采用延迟加载方式,生成的sql如下(借用延迟加载文章中的图说明)

一对多关系Lazy加载

[NHibernate]N+1 Select查询问题分析

多对多关系Lazy加载

order和Product多对多关系,此时生成的sql语句

[NHibernate]N+1 Select查询问题分析

你可以看到此时生成的sql语句是通过left outer join(左外连接)进行关联数据表的,而此时只生成一条sql语句,明显减少了查询数据的次数。

总结

(1)select语句的数目太多,需要频繁的访问数据库,会影响检索性能。此时可采用sql中的左外连接查询,在一条sql语句中将所有数据查询出来,并且减少了select的次数。
(2)在应用逻辑只需要访问Customer对象,而不需要访问Order对象的场合,加载Order对象完全是多余的操作,这些多余的Order对象白白浪费了许多内存空间。
为了解决以上问题,Hibernate提供了其他两种检索策略:延迟检索策略和迫切左外连接检索策略。延迟检索策略能避免多余加载应用程序不需要访问的关联对象,迫切左外连接检索策略则充分利用了SQL的外连接查询功能,能够减少select语句的数

这里就N+1 select问题做一下特别说明,有问题,如果不想办法解决,总觉得有个疙瘩,早解决,早有好心情。通过本篇的分析,你是不是也对n+1的问题豁然开朗?这也怪我了,当时对那一块理解确实有误,再次感到很抱歉。