NHibernate父母,与期货的儿童集合

时间:2011-04-08 22:28:55

标签: nhibernate future

我有这样的设置:父,有一个Child的集合。

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

HQL:

(“From Parent”)。Future();

(“From Child”)。Future();

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

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

版本3.1.0.400

1 个答案:

答案 0 :(得分:5)

执行将来的查询时,将所有Parent和Child对象拉入第一级缓存。 Parent对象包含一个需要填充的惰性集合。要填充集合,NHibernate必须查询数据库。 (我们将在一秒钟内了解原因。)查询返回Child对象,这些子对象已经在L1缓存中。因此,这些对象用于填充集合。

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

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

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

使用未来的Child对象查询将完全是可选的,因为您正在使用父项获取子项。由于连接提取,您将获得重复的父对象,尽管它们将是同一个对象。 (您正在数据库中进行内部联接,并为每个子行返回父行的一个副本。)您可以通过迭代parent.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>