NHibernate的 - HQL“加入取”与SetMaxResults抛出错误(NHiberna

2019-08-04 03:08发布

我试图运行简单的查询:

string queryStr = "select b " +
                  "from Blog b " +
                  "left outer join fetch b.BlogComments bc";

IList<Blog> blogs = Session.CreateQuery(queryStr)
    .SetMaxResults(10)
    .List<Blog>();

但它引发以下错误:

System.ArgumentNullException: Value cannot be null.
Parameter name: source

但是,如果我请从HQL的“取”,它工作正常。 另外,如果我留在取,但除去SetMaxResults它也能正常工作。 这件事情做的抓取+ SetMaxResults组合。

我想急于加载子集优化查询和防止SELECT N + 1的问题。 我使用NHibernate 3.3.1.4000与MySQL数据库。


我的映射:

public class BlogMap : ClassMapping<Blog>
{
    public BlogMap ()
    {
        // other properties (snip)....

        Set(x => x.BlogComments, x =>
        {
            x.Inverse(true);
            x.Cascade(Cascade.All | Cascade.DeleteOrphans);
            x.Lazy(CollectionLazy.Extra);
            x.Key(k => { k.Column("BlogId"); });
        }, x => x.OneToMany());
    }
}


public class BlogCommentMap : ClassMapping<BlogComment>
{
    public BlogCommentMap ()
    {
        // other properties (snip)....

        ManyToOne(x => x.Blog, x =>
        {
            x.Column("BlogId");
            x.NotNullable(true);
        });
    }
}

堆栈跟踪的要求:

[ArgumentNullException: Value cannot be null.
Parameter name: source]
   System.Linq.Enumerable.ToList(IEnumerable`1 source) +4206743
   NHibernate.Engine.QueryParameters.CreateCopyUsing(RowSelection selection) +178
   NHibernate.Hql.Ast.ANTLR.QueryTranslatorImpl.List(ISessionImplementor session, QueryParameters queryParameters) +210
   NHibernate.Engine.Query.HQLQueryPlan.PerformList(QueryParameters queryParameters, ISessionImplementor session, IList results) +369
   NHibernate.Impl.SessionImpl.List(String query, QueryParameters queryParameters, IList results) +301

[GenericADOException: Could not execute query[SQL: SQL not available]]
   NHibernate.Impl.SessionImpl.List(String query, QueryParameters queryParameters, IList results) +351
   NHibernate.Impl.SessionImpl.List(String query, QueryParameters parameters) +282
   NHibernate.Impl.QueryImpl.List() +162
   WebApp.Repositories.BlogRepository.SearchBlogs(SearchBlogs search) in C:\...path...\BlogRepository.cs:43
   WebApp.Controllers.HomepageController.Index(Int32 page) in C:\...path...\Controllers\HomepageController.cs:54
   lambda_method(Closure , ControllerBase , Object[] ) +101
   System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +17
   System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +208
   System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +27
   System.Web.Mvc.<>c__DisplayClass15.<InvokeActionMethodWithFilters>b__12() +55
   System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) +263
   System.Web.Mvc.<>c__DisplayClass17.<InvokeActionMethodWithFilters>b__14() +19
   System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +191
   System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +343
   System.Web.Mvc.Controller.ExecuteCore() +116
   System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +97
   System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +10
   System.Web.Mvc.<>c__DisplayClassb.<BeginProcessRequest>b__5() +37
   System.Web.Mvc.Async.<>c__DisplayClass1.<MakeVoidDelegate>b__0() +21
   System.Web.Mvc.Async.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _) +12
   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
   System.Web.Mvc.<>c__DisplayClasse.<EndProcessRequest>b__d() +50
   System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f) +7
   System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action) +22
   System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +60
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +8970061
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +184

Answer 1:

你遇到的是一个错误......但在这种情况下,它可能是一个好兆头 ! ;)因为分页( SetFirstResult()SetMaxResults()上查询获取父和其收藏将回到意想不到的效果)。 比方说,这

数据库表包含此:

  • BlogA
    • BlogCommentA1
    • BlogCommentA2
    • BlogCommentA3
  • BlogB
    • BlogCommentB1
    • BlogCommentB2

QueryOver语法(这是工作)做与上述相同HQL:

var blogs = session.QueryOver<Blog>()
  .Fetch(c => c.BlogComment).Eager // collection in one SQL
  .Skip(0).Take(2) // paging, but weird...
  .List<Blog>();

.Skip(0).Take(2) -第一页,选择造成这种两行但ONE BlogA:

| BlogA | BlogCommentA1
| BlogA | BlogCommentA2

.Skip(2).Take(2) -下一个页面,怪异......再次BlogA

| BlogA | BlogCommentA3
| BlogB | BlogCommentB1

这是最有可能不是我们想要的。

建议:去SELECT 1 + 1

在这种情况下,最可靠的是做仅在父对象(分页Blog )。 然后,当我们在NHibernate的会话中的所有博客( .Skip(2).Take(2)我们将只调用一次选择他们孩子( BlogComments )。

最简单的方法设定的batch-size="x" ,其中x是接近平时页尺寸(例如25或50)的一个数字。

.BatchSize(25)

NHibernate的文件19.1.5。 使用批量抓取解释细节



Answer 2:

这看起来像一个bug无论是在QueryParameters.CreateCopyUsing(),还是其他什么东西是不正确创建原始QueryParameters。 总之,使用SetMaxResults()的系列外,将强制分页要应用客户端,而不是在数据库中,这可能不是你想要的。

您propably要重写,要使用上产生10博客ID的子查询的并且在加入外部查询获取。



文章来源: NHibernate - HQL “join fetch” with SetMaxResults throws error