将投影添加到NHibernate标准会阻止它执行默认实体选择
-
10-07-2019 - |
题
我正在编写NHibernate标准,选择支持分页的数据。我正在使用SQL Server 2005(+)中的COUNT(*) OVER()
表达式来获取可用行的总数,如建议。我需要这个数字才能计算出总共有多少页面。这个解决方案的优点在于我不需要执行第二个查询来获取行计数。
但是,我似乎无法编写工作标准(Ayende只提供HQL查询)。
这是一个显示我想要的SQL查询,它运行得很好。请注意,我故意省略了实际的分页逻辑以关注问题:
SELECT Items.*, COUNT(*) OVER() AS rowcount
FROM Items
这是HQL:
select
item, rowcount()
from
Item item
请注意,rowcount()
函数在自定义NHibernate方言中注册,并在SQL中解析为item
。
要求是使用条件表达查询。不幸的是,我不知道如何做对:
var query = Session
.CreateCriteria<Item>("item")
.SetProjection(
Projections.SqlFunction("rowcount", NHibernateUtil.Int32));
每当我添加投影时,NHibernate都不会选择<=>(就像没有投影一样),只有<=>而我真的需要两者。另外,我似乎无法将<=>作为一个整体投影,只有它的属性,我真的不想列出所有这些属性。
我希望有人能解决这个问题。不管怎样,谢谢。
解决方案
我认为在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>();
其他提示
使用CreateMultiCriteria。
你可以执行2个简单的语句,只有一次命中DB。
我想知道为什么使用Criteria是一项要求。你不能使用session.CreateSQLQuery吗?如果你真的必须在一个查询中执行它,我会建议撤回Item对象和计数,如:
select {item.*}, count(*) over()
from Item {item}
...通过这种方式,您可以从查询中获取Item对象以及计数。如果您遇到Hibernate缓存问题,您还可以配置与本机查询关联的查询空间(实体/表缓存),以便自动清除过时的查询缓存条目。
如果我理解你的问题,我有一个解决方案。我在同样的问题上苦苦挣扎。
让我快速描述我遇到的问题,以确保我们在同一页面上。我的问题归结为分页。我想在UI中显示10条记录,但我也想知道符合过滤条件的总条记录数。我想使用NH标准API来实现这一点,但是当为行计数添加投影时,我的查询不再有效,并且我不会得到任何结果(我不记得具体的错误,但它听起来像你的'得到了。)
这是我的解决方案(复制<!>放大器;粘贴我当前的生产代码)。注意<!> quot; SessionError <!> quot;是我正在检索分页数据的业务实体的名称,根据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];
所以我创建了我的基本标准,带有可选的限制。然后我克隆它,并将行计数投影添加到CLONED标准。请注意,我在之前克隆它我添加了分页限制。然后我设置一个IMultiCriteria来包含原始和克隆的ICriteria对象,并使用IMultiCriteria来执行它们。现在,我从原始ICriteria获取了我的分页数据(我只是通过线路拖动了我需要的数据),还有一个原始计数,表明有多少实际记录符合我的标准(用于显示或创建分页链接,或其他任何内容)。这个策略对我有用。我希望这有用。
我建议您在会话中调用SetResultTransformer()来调查自定义结果转换器。
在类映射中创建公式属性:
<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;