8

我正在编写一个选择支持分页的数据的 NHibernate 标准。正如Ayende Rahien所建议的,我正在使用COUNT(*) OVER()SQL Server 2005(+) 中的表达式来获取可用行的总数。我需要这个数字才能计算总共有多少页。这个解决方案的美妙之处在于我不需要执行第二个查询来获取行数。

但是,我似乎无法编写工作条件(Ayende 仅提供 HQL 查询)。

这是一个 SQL 查询,它显示了我想要的,它工作得很好。请注意,我故意省略了实际的分页逻辑以专注于问题:

SELECT Items.*, COUNT(*) OVER() AS rowcount
FROM Items

这是HQL:

select
    item, rowcount()
from 
    Item item

请注意,该rowcount()函数在自定义 NHibernate 方言中注册并解析为COUNT(*) OVER()SQL。

要求是使用标准来表达查询。不幸的是,我不知道如何正确处理:

var query = Session
    .CreateCriteria<Item>("item")
    .SetProjection(
       Projections.SqlFunction("rowcount", NHibernateUtil.Int32));

每当我添加投影时,NHibernate 都不会选择item(就像没有投影一样),rowcount()而我确实需要两者。另外,我似乎无法item整体进行项目,只有它的属性,我真的不想列出所有这些。

我希望有人对此有解决方案。不管怎么说,还是要谢谢你。

4

6 回答 6

5

我认为这在 Criteria 中是不可能的,它有一些限制。

您可以在后续查询中获取 id 并加载项目:

var query = Session
    .CreateCriteria<Item>("item")
    .SetProjection(Projections.ProjectionList()
       .Add(Projections.SqlFunction("rowcount", NHibernateUtil.Int32))
       .Add(Projections.Id()));

如果您不喜欢它,请使用 HQL,您也可以在那里设置最大结果数:

IList<Item> result = Session
    .CreateQuery("select item, rowcount() from item where ..." )
    .SetMaxResult(100)
    .List<Item>();
于 2009-11-10T14:08:35.297 回答
0

如果我正确理解你的问题,我有一个解决方案。我在同样的问题上挣扎了很多。

让我快速描述一下我遇到的问题,以确保我们在同一页面上。我的问题归结为分页。我想在 UI 中显示 10 条记录,但我也想知道与过滤条件匹配的记录总数。我想使用 NH 条件 API 来完成此操作,但是在添加行数投影时,我的查询不再起作用,并且我不会得到任何结果(我不记得具体的错误,但听起来像你'得到)。

这是我的解决方案(从我当前的生产代码中复制并粘贴)。请注意,“SessionError”是我正在为其检索分页数据的业务实体的名称,根据 3 个过滤条件:IsDev、IsRead 和 IsResolved。

ICriteria crit = CurrentSession.CreateCriteria(typeof (SessionError))
    .Add(Restrictions.Eq("WebApp", this));

if (isDev.HasValue)
    crit.Add(Restrictions.Eq("IsDev", isDev.Value));

if (isRead.HasValue)
    crit.Add(Restrictions.Eq("IsRead", isRead.Value));

if (isResolved.HasValue)
    crit.Add(Restrictions.Eq("IsResolved", isResolved.Value));

// Order by most recent
crit.AddOrder(Order.Desc("DateCreated"));

// Copy the ICriteria query to get a row count as well
ICriteria critCount = CriteriaTransformer.Clone(crit)
    .SetProjection(Projections.RowCountInt64());
critCount.Orders.Clear();

// NOW add the paging vars to the original query
crit = crit
    .SetMaxResults(pageSize)
    .SetFirstResult(pageNum_oneBased * pageSize);

// Set up a multi criteria to get your data in a single trip to the database
IMultiCriteria multCrit = CurrentSession.CreateMultiCriteria()
    .Add(crit)
    .Add(critCount);

// Get the results
IList results = multCrit.List();

List<SessionError> sessionErrors = new List<SessionError>();
foreach (SessionError sessErr in ((IList)results[0]))
    sessionErrors.Add(sessErr);

numResults = (long)((IList)results[1])[0];

所以我创建了我的基本标准,带有可选的限制。然后我克隆它,并将行计数投影添加到克隆标准。请注意,我在添加分页限制之前克隆了它。然后我设置了一个 IMultiCriteria 来包含原始和克隆的 ICriteria 对象,并使用 IMultiCriteria 来执行它们。现在我有来自原始 ICriteria 的分页数据(我只将我需要的数据拖过线路),还有多少实际记录符合我的标准的原始计数(对于显示或创建分页链接或其他有用)。这个策略对我来说效果很好。我希望这是有帮助的。

于 2009-11-10T19:53:08.823 回答
0

我建议通过在会话中调用 SetResultTransformer() 来调查自定义结果转换器。

于 2009-11-18T13:46:56.957 回答
0

使用 CreateMultiCriteria。

您可以通过这种方式执行 2 个简单的语句,只需一次命中 DB。

于 2009-11-01T16:45:34.907 回答
0

在类映射中创建公式属性:

<property name="TotalRecords" formula="count(*) over()" type="Int32" not-null="true"/>;

IList<...> result = criteria.SetFirstResult(skip).SetMaxResults(take).List<...>();
totalRecords = (result != null && result.Count > 0) ? result[0].TotalRecords : 0;
return result;
于 2010-04-09T14:56:03.773 回答
0

我想知道为什么使用 Criteria 是一项要求。你不能使用 session.CreateSQLQuery 吗?如果您真的必须在一个查询中执行此操作,我会建议撤回 Item 对象和计数,例如:

select {item.*}, count(*) over() 
from Item {item}

...通过这种方式,您可以从查询中获取 Item 对象以及计数。如果您遇到 Hibernate 的缓存问题,您还可以配置与本机查询关联的查询空间(实体/表缓存),以便自动清除陈旧的查询缓存条目。

于 2009-11-02T23:50:07.537 回答