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

Ken*_*Ken 0 nhibernate future

我有这个设置:父,有一个儿童的集合.

class Parent {
    IList<Child> Childs { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

HQL:

("来自父母").期货();

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

foreach(Parent p in result) {
    foreach(Child c in p.Childs) {
    }
}
Run Code Online (Sandbox Code Playgroud)

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

版本3.1.0.400

Jam*_*acs 5

执行将来的查询时,将所有父对象和子对象都拉入第一级缓存.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>();
Run Code Online (Sandbox Code Playgroud)

当您使用父级获取子项时,使用future的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>
Run Code Online (Sandbox Code Playgroud)

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

<bag name="Children" cascade="all-delete-orphan" batch-size="10">
  <key column="ParentId"/>
  <one-to-many class="Child"/>
</bag>
Run Code Online (Sandbox Code Playgroud)