【问题标题】:NHibernate parent, child collection with futuresNHibernate 父、子集合与期货
【发布时间】:2011-11-08 12:22:05
【问题描述】:

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

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

HQL:

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

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

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

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

版本 3.1.0.400

【问题讨论】:

    标签: nhibernate future


    【解决方案1】:

    当您执行未来查询时,您会将所有父对象和子对象拉入一级缓存。 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>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-30
      • 1970-01-01
      • 2017-05-28
      • 1970-01-01
      相关资源
      最近更新 更多