5

私は最も単純なクエリを実行しようとしています:

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を削除すると、正常に機能します。これは、fetch+SetMaxResultsの組み合わせと関係があります。

クエリを最適化し、SELECT N + 1の問題を防ぐために、子コレクションを熱心にロードしようとしています。MySQLデータベースでNHibernate3.3.1.4000を使用しています。


私のマッピング:

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
4

2 に答える 2

3

あなたが経験しているのはバグです...しかし、この場合、それは良い兆候かもしれません!;)クエリをフェッチする親とそのコレクションのページング( SetFirstResult()、 )は、予期しない結果を返すためです。SetMaxResults()たとえば、

DBテーブルには次のものが含まれます。

  • 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)-最初のページ、選択するとこの2行になりますが、1つのBlogA

| BlogA | BlogCommentA1
| BlogA | BlogCommentA2

.Skip(2).Take(2)-次のページ、奇妙な...再びBlogA

| BlogA | BlogCommentA3
| BlogB | BlogCommentB1

そして、これはおそらく私たちが望んでいないことです。

提案:SELECT 1+1に行く

この場合の最も信頼できるのは、親オブジェクト(Blog)でのみページングを実行することです。そして、NHibernateセッションですべてのブログ( )がある場合、すべての子.Skip(2).Take(2)に対してselectを1回だけ呼び出します( )。BlogComments

最も簡単な方法は、を設定することbatch-size="x"です。ここで、xは通常のページサイズに近い数値です(例:25または50)。

.BatchSize(25)

NHibernateドキュメント19.1.5。バッチフェッチの使用で詳細を説明

于 2012-12-15T17:09:16.123 に答える
2

This looks like a bug either in QueryParameters.CreateCopyUsing(), or something else is not creating the original QueryParameters correctly. Anyway, using SetMaxResults() with collection joins will force the paging to be applied client side, not in the database, which may not be what you want.

You propably want to rewrite that to use a IN on a subquery that yields 10 blog id and have the join fetching in the outer query.

于 2012-12-15T15:08:53.077 に答える