2011-04-08 85 views
0

我有这样的设置:Parent,带有Child的集合。NHibernate的父级,期货子集合

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

HQL:

( “从父”)未来();

(“From Child”)。Future();

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

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

版本3.1.0.400

回答

5

当你执行今后的查询,你拉的所有父母与子女对象为1级高速缓存。父对象包含需要填充的惰性集合。为了填充集合,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>(); 

使用未来的子对象查询将是完全可选的,因为您正在使用父项获取子项。由于联合提取,您将获得重复的父对象,尽管它们将是同一个对象。 (你正在数据库中做一个内部连接,并为每个子行返回一个父行的副本。)你可以通过在parents.Distinct()上迭代来摆脱这些。

如果您始终想要使用相应的Parent获取子对象,则还可以在您的父映射中使用fetch =“join”。

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

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

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