【问题标题】:Partially Populate Child Collection with NHibernate使用 NHibernate 部分填充子集合
【发布时间】:2012-09-04 01:12:02
【问题描述】:

我已经为此苦苦挣扎了一段时间,但似乎无法弄清楚......

我有一个BlogPost 类,它有一个Comments 的集合,每个cmets 都有一个DatePosted 字段。

我需要做的是查询 BlogPost 并返回部分加载的 Comments 集合,比如说 2009 年 8 月 1 日发布的所有 cmets。

我有这个查询:

BlogPost post = session.CreateCriteria<BlogPost>()
    .Add(Restrictions.Eq("Id", 1))
    .CreateAlias("Comments", "c")
    .Add(Restrictions.Eq("c.DatePosted", new DateTime(2009, 8, 1)))
    .UniqueResult<BlogPost>();

当我运行此查询并检查生成的 sql 时,它首先对 BlogPost 表运行查询,加入具有正确日期限制的 Comment 表,然后仅在 @ 上运行第二个查询987654329@ 返回所有内容的表。

结果是BlogPost 类的Comments 集合完全填满了!

我做错了什么?

如果有人需要更多信息,我有代码示例...!

【问题讨论】:

    标签: c# nhibernate


    【解决方案1】:

    有一个结果转换器,见the documentation.

    引用:

    请注意,小猫收藏 由返回的 Cat 实例 前两个查询不是 按标准预先过滤!如果你 希望只取回那些 符合条件,您必须使用 SetResultTransformer(CriteriaUtil.AliasToEntityMap).

    IList cats =
    sess.CreateCriteria(typeof(Cat))
        .CreateCriteria("Kittens", "kt")
            .Add( Expression.Eq("Name", "F%") )
        .SetResultTransformer(CriteriaUtil.AliasToEntityMap)
        .List();
    

    您还可以使用通过session.EnableFilter(name) 激活的过滤器。

    有一个similar question here

    【讨论】:

      【解决方案2】:

      您并没有真正做错任何事情 - hibernate 只是不能那样工作。

      如果您从 BlogPost 导航到 Comments,Hibernate 将根据您指定的关联映射填充 cmets,而不是您用来检索 BlogPost 的查询。大概你的映射只是在做一个连接 在关键列上。您可以使用您正在寻找的filter to get the effect。但我认为这仍然会获取所有 cmets,然后进行后过滤。

      更简单的,只要查询你想要的:

      List<Comments> comments = session.CreateCriteria<BlogPost>()
                  .Add(Restrictions.Eq("Id", 1))
                  .CreateAlias("Comments", "c")
                  .Add(Restrictions.Eq("c.DatePosted", new DateTime(2009, 8, 1)))
                  .list();
      

      这实际上只会返回指定日期的 cmets。 如果它让你感觉更好,你可以这样设置它们:

      post.setComments(comments); //having already retreived the post elsewhere
      

      当我第一次遇到这种行为时,我也很惊讶。这似乎是一个错误,但我被设计成这样。

      【讨论】:

      • NH 从不进行任何后过滤。
      【解决方案3】:

      感谢您的回复,我想我有点理解它的设计原因,但我原以为会有一个内置方法来实现这一点,您的解决方案有效,但感觉有点像黑客!

      我的问题是,如果不过滤,子集合会非常庞大​​(我给出的帖子和 cmets 示例是为了保护无辜者的姓名!)现在我可以每次都将所有数据拉回来。

      我已经对此运行了 Sql Profiler,但它仍在拉回所有数据。 当我运行以下代码时,第一个查询执行您的预期,只有一个帖子返回,但是一旦执行第二个查询,两个查询进入数据库,第一个检索过滤的 cmets(宾果游戏!),然后用所有 cmets 填充 post.Comments 属性,这正是我想要避免的!

              var post = session.CreateCriteria<BlogPost>()
                  .Add(Restrictions.Eq("Id", 1))
                  .UniqueResult<BlogPost>();
      
              var comments = session.CreateCriteria<Comment>()
                  .Add(Restrictions.Eq("BlogPostId", 1))
                  .Add(Restrictions.Eq("DatePosted", new DateTime(2009, 8, 1)))
                  .List<Comment>();
      
              post.Comments = comments;
      

      这很奇怪,它不像我在枚举 post.Comments 列表,那么它为什么要填充它?!这是我的课程和地图:

      public class BlogPostMap : ClassMap<BlogPost>
      {
          public BlogPostMap()
          {
              Id(b => b.Id);
              Map(b => b.Title);
              Map(b => b.Body);
              HasMany(b => b.Comments).KeyColumnNames.Add("BlogPostId");
          }
      }
      public class CommentMap : ClassMap<Comment>
      {
          public CommentMap()
          {
              Id(c => c.Id);
              Map(c => c.BlogPostId);
              Map(c => c.Text);
              Map(c => c.DatePosted);
          }
      }
      
      public class BlogPost
      {
          public virtual int Id { get; set; }
          public virtual string Title { get; set; }
          public virtual string Body { get; set; }
          public virtual IList<Comment> Comments { get; set; }
      }
      public class Comment
      {
          public virtual int Id { get; set; }
          public virtual int BlogPostId { get; set; }
          public virtual string Text { get; set; }
          public virtual DateTime DatePosted { get; set; }
      }
      

      有什么想法吗?

      【讨论】:

      • 使用 SetResultTransformer。看我的回答。
      【解决方案4】:

      我同意手动填充集合感觉像是一种黑客行为。

      您可以改用自定义加载程序。像这样的:

      <query name="loadComments">
      <return alias="comments" class="Comment"/>
      <load-collection alias="comments" role="Post.comments"/>
      from Comments c where c.Id = ? and c.DatePosted = SYSDATE
      </query>
      

      此外,如果您想要更多控制权,可以使用 sql-query。 当我无法进入休眠状态来生成我想要的查询时,我偶尔会倾向于编写自定义加载器。无论如何,不​​知道为什么我一开始就没有想到这一点。

      【讨论】:

        【解决方案5】:

        使 Comments Collection 变得懒惰,这样当您获取 BlogPost 时,hibernate 就不会获取它。然后对评论集合使用过滤器。

        cmets = session.CreateFilter(blogPost.Comments, ... ).List();

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-01-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多