使用NHibernate部分填充子集合

我一直在努力解决这个问题,似乎无法弄明白……

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

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

我有这个问题:

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

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

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

我究竟做错了什么?

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

有一个结果变换器,请参阅文档。

引用:

请注意,前两个查询返回的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)激活的filter。

这里有一个类似的问题 。

你真的没有做错任何事 – hibernate不会那样。

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

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

 List comments = session.CreateCriteria() .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 

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

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

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

我在这上面运行了Sql Profiler,它仍然将所有数据都拉回来。 当我运行以下代码时,第一个查询执行您所期望的,只有一个post返回,但是一旦执行第二个查询,两个查询将转到数据库,第一个检索过滤的注释(bingo!),然后第二个用所有评论填充post.Comments属性,正是我想要避免的!

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

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

 public class BlogPostMap : ClassMap { 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 { 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 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; } } 

有任何想法吗?

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

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

    from Comments c where c.Id = ? and c.DatePosted = SYSDATE  

此外,如果您想要更多控制,可以使用sql-query。 当我无法通过hibernate生成我想要的查询时,我偶尔会弯腰编写自定义加载器。 无论如何,不​​知道为什么我没有想到这一点。

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

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