急于加载带有nHibernate的树重新查询叶节点

急于加载带有nHibernate的树重新查询叶节点,nhibernate,orm,tree,nhibernate-mapping,eager-loading,Nhibernate,Orm,Tree,Nhibernate Mapping,Eager Loading,在我宣布NHibernate框架崩溃或我自己疯了之前,我正在寻找一些专业知识 我正在尝试用NHibernate加载一个自引用树,并且可以成功地加载大多数子列表,但是我似乎无法让它与叶节点一起工作。我的问题是: "select p from Proposal p join fetch p.Structures s join fetch s.theChildrenList c " + "where p._persistenceId = :p1 and s.theProposal = :p1 and

在我宣布NHibernate框架崩溃或我自己疯了之前,我正在寻找一些专业知识

我正在尝试用NHibernate加载一个自引用树,并且可以成功地加载大多数子列表,但是我似乎无法让它与叶节点一起工作。我的问题是:

"select p from Proposal p join fetch p.Structures s join fetch s.theChildrenList c " +
"where p._persistenceId = :p1 and s.theProposal = :p1 and c.theProposal = :p1")
.SetParameter("p1", aProposalId)
.SetResultTransformer(new DistinctRootEntityResultTransformer()).
UniqueResult<Proposal>();
“从建议中选择p p加入获取p.Structures s加入获取s.theChildrenList c”+
“其中p._persistenceId=:p1和s.theProposal=:p1和c.theProposal=:p1”)
.SetParameter(“p1”,aProposalId)
.SetResultTransformer(新的DistinctRootEntityResultTransformer())。
UniqueResult();
这将正确返回所有记录,但不会正确填充叶节点的childrenList(定义为空)。取而代之的是,当我得到一个代理并搜索树时,数千个毫无意义的零记录查询

我试过: 1.使用左联接而不是联接 2.使儿童列表不懒惰,fetch=“join”并删除hql(作为概念证明,性能下降在其他地方是不可接受的) 3.弄乱了我在hibernate论坛中看到有人使用的not found属性

所有这些都给了我同样的结果。。。一个包含所有数据的大型查询(良好)和数千个不返回数据的小型查询(不良)。有什么想法吗

我使用的是NHibernate 2.2,下面是映射文件的相关部分供参考:

  <many-to-one    name="theParentStructure"
                  column="PARENT_STRUCTURE_ID"
                  class="Structure"
                  access="field"
                  update="false"
                  insert="false"
                  not-found="ignore"/>

  <bag            name="theChildrenList"
                  generic="true"
                  table="STRUCTURE"
                  access="field"
                  cascade="all-delete-orphan"
                  inverse="true" fetch="join" lazy="false">  <--Both with and without the last two properties
     <key column="PARENT_STRUCTURE_ID" />
     <one-to-many class="Structure"/>
  </bag>


我很久以前就对这个话题感兴趣了,所以请不要把我说的每一句话都视为理所当然(也许有更多经验的人可以纠正我的错误)

上次我检查所描述的情况是一个相当大的问题,因为
Nhibernate
正在为每个
级别的子实体生成单独的查询。如果你事先知道,你的树的深度不会非常大,如果没有孩子们的
急切加载
,你可能无法生存。。。但是,如果你的结构在深度上没有限制,你可能应该考虑其他选择

假设您使用的是SqlServer

我发现一个很容易实现的解决方案是使用
递归自连接
/
CTE
。此解决方案涉及切换到
存储过程
作为查询源,并在代码中手动重新创建层次结构。要获取整个树,您只需要一个db查询(可以包括各种过滤器和子查询+排序),并且您仍然可以对所有
CRUD
操作重复使用
NHibernate
映射。此解决方案的一个缺点是,以后必须在数据库端维护查询代码(列和映射更改等)

改进的前序树遍历算法

这是我的
树持久化的理想解决方案,因为它不需要任何
存储过程
,并且可以与
NHibernate
标准API
完美集成(这对我来说是一个巨大的优势,因为我可以在代码中自由创建高级和可重用的过滤器)。从性能角度来看,所有选择几乎都是免费的-您需要在每次更改时重新计算树,从而将平衡移到插入、更新和删除操作上-但这可以通过
hql
这样做(伪代码):

HQLNamedQuery hql=newhqlnamedquery();
hql.Query=“UPDATE”+typeof(THierarchy).FullName+
“设置TraversalLeft=(TraversalLeft+:traversalChange)”+

“其中BaseNodeId=:BaseNodeId和TraversalLeft>:minTr和TraversalLeft我认为根本原因是NHibernate无法知道查询返回层次结构中的所有记录。也就是说,它不知道某个节点是叶节点。它怎么可能知道


我的快速而肮脏的解决方案是向提案中添加一个方法,选择lead节点并用一个新的空集合替换代理集合。基本上,您可以通过调用此方法通知提案它已完全填充。

Hmmm,关于预订单树遍历的有趣想法,但遍历树不是我的问题,它得到了树!我实际上已经研究了您的第一个解决方案(好吧,oracle版本的'LinkByPrevior'),但它似乎并没有解决nHibernate重新查询所有叶节点的根本问题。我当前的代码获得了所有正确的数据,nHibernate似乎不理解没有子节点时缺少行是因为它们“不存在”,而不是我们还没有查询它们…我更新了我的答案-我添加了一个链接到一篇关于NHibernate中映射树的好文章,但老实说,我认为如果没有“hacks/tricks”,您无法轻松阻止额外的查询。另一方面,为什么您可以自己重新创建层次结构并更新映射,以便不通过代理查询子实体?我希望不必重新创建结构的基础,因为它是整个系统的基础,但我越看这个问题,似乎更多的可能是答案(这里至少还有一个月重新编码一个遗留的应用程序……)我希望能有更多类似Ayende在这里所说的东西:这样做会不会让nHibernate误以为所有叶节点都是脏的,并让它发出等量的update语句?我不这么认为。即使它们被标记为脏的,它们也不会有任何成员,因此不会有任何数据库调用e已发出。请检查NHibernateUtil.IsInitialized以确定集合是否为代理,如果是代理,则将其分配给空集合。
HQLNamedQuery hql = new HQLNamedQuery();
hql.Query = "UPDATE " + typeof(THierarchy).FullName +
    " SET TraversalLeft = (TraversalLeft + :traversalChange) " +
    " WHERE BaseNodeId = :baseNodeId AND TraversalLeft > :minTr AND TraversalLeft <= :maxTr";