0

我有这个设置:父母,有一个孩子的集合。

class Parent {
    IList<Child> Childs { get; set; }
}

总部:

("来自父母").Future();

("来自孩子").Future();

foreach(Parent p in result) {
    foreach(Child c in p.Childs) {
    }
}

这给出了经典的 N+1 问题。两个SQL语句一次往返发送到服务器,所以所有数据都存在于一级缓存中,为什么NH仍然存在每个孩子的SQL。

版本 3.1.0.400

4

1 回答 1

5

执行未来查询时,会将所有父对象和子对象拉入一级缓存。Parent 对象包含一个需要填充的惰性集合。为了填充集合,NHibernate 必须查询数据库。(我们马上就会知道原因。)查询返回子对象,而这些子对象已经在 L1 缓存中。所以这些对象用于填充集合。

现在为什么 NHibernate 必须查询数据库来填充 Childs 集合?您可以在集合上有一个“where”子句,用 IsDeleted==true 过滤掉子对象。您可以在过滤掉某些子对象的 EventListener 中有代码。基本上有很多事情会发生,NHibernate 不能对 Parent 和 Child 对象之间的关系做出任何假设。

您可以通过在 HQL 或映射中指定获取策略来为其提供足够的信息。在 HQL 中,您可以编写:

var parents = session.CreateQuery("from Parent p join fetch p.Childs").Future<Parent>();

当您与父母一起获取孩子时,使用未来的孩子对象查询将是完全可选的。由于连接提取,您将获得重复的 Parent 对象,尽管它们将是同一个对象。(您正在数据库中进行内部连接并为每个子行返回一个父行的副本。)您可以通过遍历 parents.Distinct() 来摆脱这些。

如果您总是想使用相应的 Parent 获取 Child 对象,您还可以在 Parent 映射中使用 fetch="join" 。

<bag name="Children" cascade="all-delete-orphan" fetch="join">
  <key column="ParentId"/>
  <one-to-many class="Child"/>
</bag>

如果这些选项都不适用于您的方案,您可以在集合映射上指定批量大小。当您点击 parent.Childs 时,您仍将执行数据库查询,但 NHibernate 将急切地初始化任何其他集合代理。

<bag name="Children" cascade="all-delete-orphan" batch-size="10">
  <key column="ParentId"/>
  <one-to-many class="Child"/>
</bag>
于 2011-04-08T23:23:46.117 回答