使用NHibernate部分填充子集合

时间:2009-08-07 14:20:18

标签: c# nhibernate

我一直在努力解决这个问题,似乎无法弄清楚......

我有一个BlogPost课程,其中包含Comments个集合,每个评论都有一个DatePosted字段。

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

我有这个问题:

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表,然后运行第二个查询就在返回所有内容的Comment表上。

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

我做错了什么?

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

5 个答案:

答案 0 :(得分:2)

有一个结果转换器,请参阅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

答案 1 :(得分:0)

你真的没有做错任何事 - hibernate就是这样不行。

如果您从BlogPost导航到评论,Hibernate将根据您指定的关联映射填充注释,而不是您用于检索BlogPost的查询。据推测,您的映射只是在进行连接 在关键栏目上。您可以使用您正在寻找的filter to get the effect。但我认为仍然可以获取所有评论,然后进行后期过滤。

更简单地说,只需查询您想要的内容:

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();

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

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

当我第一次遇到它时,我也对这种行为感到惊讶。这似乎是一个错误,但我已经被告知它的设计。

答案 2 :(得分:0)

感谢您的回复,我想我有点理解为什么它的设计,但我会想到会有一个内置的方法来启用它,你的解决方案有效,但感觉有点像黑客!

我的问题是,如果没有过滤,子集合是巨大的(我给出的帖子和评论的例子是为了保护无辜的名字!)现在我可以每次都将所有数据拉回来。

我在这上面运行了Sql Profiler,它仍然将所有数据都拉回来。 当我运行以下代码时,第一个查询执行您所期望的,只有一个帖子返回,但是一旦执行第二个查询,两个查询将转到数据库,第一个检索过滤的注释(bingo!),然后第二个用所有评论填充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; }
}

任何想法?

答案 3 :(得分:0)

我同意手动填充该集合感觉就像是一个黑客。

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

<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。 当我无法通过hibernate生成我想要的查询时,我偶尔会弯腰编写自定义加载器。无论如何,不​​知道为什么我一开始就没有想到这一点。

答案 4 :(得分:0)

使评论集合变得懒惰,以便当您获得BlogPost时,hibernate不会获取它。然后在Comments集合上使用过滤器。

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