【问题标题】:Pagination with total row count in NHibernateNHibernate中总行数的分页
【发布时间】:2011-08-23 11:51:04
【问题描述】:

我正在尝试使用 HQL 对简单查询进行分页,并将总行数作为同一查询的一部分检索。

我的查询很简单...

var members = UnitOfWork.CurrentSession.CreateQuery(@"
    select m
    from ListMember as m
    join fetch m.Individual as i") 
    .SetFirstResult(pageIndex*pageSize)
    .SetMaxResults(pageSize)
    .List<ListMember>();

Individual 被映射为 ListMember 类上的多对一。这很好用。分页按预期工作并生成以下 Sql...

SELECT   TOP ( 10 /* @p0 */ ) DirPeerG1_1_0_,
                 Director1_0_1_,
                 Director2_1_0_,
                 Forename2_0_1_,    
                 Surname0_1_
FROM     (SELECT listmember0_.DirPeerGrpMemberID    as DirPeerG1_1_0_,
             listmember1_.DirectorKeyID         as Director1_0_1_,
             listmember0_.DirectorKeyId         as Director2_1_0_,
             listmember1_.Forename1             as Forename2_0_1_,
             listmember1_.Surname               as Surname0_1_,
             ROW_NUMBER()
               OVER(ORDER BY CURRENT_TIMESTAMP) as __hibernate_sort_row
          FROM   tblMembers listmember0_
             inner join tblIndividuals listmember1_
               on listmember0_.DirectorKeyId = listmember1_.DirectorKeyID) as query
WHERE    query.__hibernate_sort_row > 10 /* @p1 */
ORDER BY query.__hibernate_sort_row

我阅读了 Ayende 发布的这篇名为 Paged data + Count(*) with NHibernate: The really easy way! 的文章,因此我尝试在我的查询中实现它。

我按照文章中的步骤添加了名为 rowcount() 的自定义 HQL 函数,并将我的查询更改为...

var members = UnitOfWork.CurrentSession.CreateQuery(@"
    select m, rowcount()
    from ListMember as m
    join fetch m.Individual as i") 
    .SetFirstResult(pageIndex*pageSize)
    .SetMaxResults(pageSize)
    .List<ListMember>();

生成的 Sql 几乎是正确的,但是它包含两次导致此错误的列之一...

System.Data.SqlClient.SqlException: 列“...”已指定 多次“查询”。

它生成的Sql长这样……

SELECT   TOP ( 10 /* @p0 */ ) 
         col_0_0_, 
         col_1_0_, 
         Director1_0_1_, 
         DirPeerG1_1_0_, 
         Director1_0_1_, 
         Director2_1_0_, 
         Forename2_0_1_,    
         Surname0_1_
FROM     (SELECT 
      listmember0_.DirPeerGrpMemberID as col_0_0_, 
      count(*) over() as col_1_0_, 
      listmember1_.DirectorKeyID as Director1_0_1_, 
      listmember0_.DirPeerGrpMemberID as DirPeerG1_1_0_, 
      listmember1_.DirectorKeyID as Director1_0_1_, 
      listmember0_.DirectorKeyId as Director2_1_0_, 
      listmember1_.Forename1 as Forename2_0_1_, 
      listmember1_.Surname as Surname0_1_, 
             ROW_NUMBER()
               OVER(ORDER BY CURRENT_TIMESTAMP) as __hibernate_sort_row
          FROM   RCMUser.dbo.tblDirPeerGrpMembers listmember0_
             inner join RCMAlpha.dbo.tblDirectorProfileDetails listmember1_
               on listmember0_.DirectorKeyId = listmember1_.DirectorKeyID) as query
WHERE    query.__hibernate_sort_row > 10 /* @p1 */
ORDER BY query.__hibernate_sort_row

由于某种原因,它在投影中包含了两次Director1_0_1_ 列,这会导致此错误。这个 Sql 非常接近我想要的,我希望那里的 NHibernate 专家可以帮助解释为什么会发生这种情况。

建议尝试

感谢@Jason 的建议。我尝试使用 .List() 方法的非通用版本来执行查询,但不幸的是,这也产生了具有重复列的相同 Sql...

var members = UnitOfWork.CurrentSession.CreateQuery(@"
    select m, rowcount()
    from ListMember as m
    join fetch m.Individual as i")
    .SetFirstResult(pageIndex * pageSize)
    .SetMaxResults(pageSize)
    .List()
    .Cast<Tuple<ListMember, int>>()
    .Select(x => x.First);

更新

如果不进入 NH 源代码,这似乎是不可能的。我的解决方案要求已经改变,我不再追求答案。

总之,解决办法是……

  • 使用 Futures 或 MultiQuery 在单个命令中执行两条语句 - 一条用于检索数据页,一条用于获取总行数。
  • 修改您的分页解决方案以不计算结果总数 - 例如连续滚动。

【问题讨论】:

  • 如何访问 ListMembers 列表中的 rowcount 字段?一起使用期货和批处理 2 查询(行数和页数)可能更容易。
  • 非常感谢@dotjoe。在尝试使用这种技术之前,它确实以有记录的方式使用期货工作,并且我得到了我想要的行为。我不喜欢的是两个查询必须具有相同的 FROM、JOIN 和 WHERE 部分才能使用相同的数据集,而且我的印象(可能是错误的)这会使一些工作加倍因此可以提高性能。我很想知道情况是否如此。也许 SqlServer 缓存了部分第一页查询,以便在批处理时用于计数查询?
  • @dotjoe。至于您关于如何检索行数的问题。我必须承认我没有考虑到它需要的那么多想法,但我希望使用自定义 ResultTransformer 以某种方式将对象元组与行计数元组分开。我什至不确定这是否可行:-(
  • 哦,至于为未来的 2 个查询重复您的努力。您可以在设置分页/投影位之前使用ToRowCountQuery() 方法。这将克隆查询,因此您将拥有相同的 where 子句。
  • 我最近遇到了ToRowCountQuery(),它肯定可以让我重用查询。但我更关心的是数据库服务器必须做的额外工作来执行一批两个查询,而不是一个也返回总行数的单个查询。

标签: nhibernate pagination hql


【解决方案1】:

嗯,一个问题是您使用的是 ListMember 类型的 List 方法。在您链接的页面的示例中,他使用 List() 返回元组列表。元组的第一项是 ListMember,第二项是行数。该 List 可能会影响您的查询,即使它确实返回也可能会引发异常。

尝试使用:

var tuples = UnitOfWork.CurrentSession.CreateQuery(@"
select m, rowcount()
from ListMember as m
join fetch m.Individual as i") 
.SetFirstResult(pageIndex*pageSize)
.SetMaxResults(pageSize)
.List();

var members = tuples.Select<Tuple<ListMember, int>, ListMember>(x => x.Item1);

但我有点同意@dotjoe。 MultiQuery 可能更容易。这是我用的。这是一个很好的 link 来自您之前链接到的同一作者 (Ayende)。

【讨论】:

  • 感谢Json的建议。这绝对值得一试,但它仍然会导致具有重复列的相同 Sql。我想我将不得不为此恢复我的 2 批查询。
猜你喜欢
  • 1970-01-01
  • 2012-08-27
  • 2013-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-12
  • 2012-08-25
  • 2021-03-01
相关资源
最近更新 更多