如何通过对象图提高分页查询的Nhibernate性能

时间:2012-03-28 08:27:10

标签: performance fluent-nhibernate pagination object-graph

如果您没有使用分页查询,NHibernate可以通过映射中的提示在单个数据库命中中加载整个对象和相关对象。但是,如果您正在使用分页,则会进行大量的数据库调用。

例如,我有一个Invoice对象和一个InvoiceLine对象,因此发票中有List个发票行。 发票的流畅映射是

HasMany(x => x.Lines)
    .Table("InvoiceLine")
    .KeyColumn("InvoiceId")
    .Fetch.Join()
    .Not.LazyLoad();

Not.LazyLoad选项非常适合在加载单个发票时在单个数据库命中中下载整个对象图...但是如果我想运行分页查询,如

_invoiceQuery.Skip(200).Take(100);

然后NHibernate首先在一次点击中加载所有100个发票...然后每次发票一次,每次点击数据库100次。

有没有办法将其减少到2次点击,即

  1. 加载发票
  2. 在(1)?
  3. 中为发票加载所有InvoiceLines

    我尝试通过将所需发票ID列表加载到查询中然后使用

    手动强制解决问题
    _invoiceQuery.Where(x => requiredIds.Contains(x.Id));
    

    但是当将Contains约束简化为SQL时,NHibernate似乎有同样的问题。

    ...或者我必须接受我需要使用其他工具吗?

1 个答案:

答案 0 :(得分:0)

根据我的经验,当使用带有急切提取的分页列表时,NHibernate并不是很好。请记住,最终您的查询将作为TSQL执行,返回一堆连接数据。现在,NHibernate能够将这些数据转换回根实体及其相关子节点。麻烦的是,NHibernate如何知道之后它从数据库返回它需要在它的TOP子句中指定多少行才能检索所需的根实体数?

同样,考虑使用Session.QueryOver和Left.JoinAlias强制连接的情况。您可以指定要检索的行数,但只是,而不是实体

通常情况下,我会以类似的方式处理您的情况 - 使用两个查询。你可以发一点代码吗?您可以通过以下几种方式进行操作:

  1. 创建初始查询,返回指定的100个发票ID的投影,然后单独点击并获取其ID包含在该列表中的发票

  2. 创建初始查询,返回指定的100个发票(lazyload the invoicelines),然后在单独的命中检索invoicelines,其中invoiceID包含在第一个列表的ID中。您应该能够两次点击数据库并在后面的代码中将父/子实体结合起来。但是对于手工合并的实体要小心一点 - 如果你打算做任何CRUD,那么首先从db中获取个体实体可能是值得的

  3. 如果您只想报告数据视图(即没有CUD操作),可以考虑映射到自定义数据库视图