在宣布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>();
这将正确返回所有记录,但不能正确填充叶节点的childrenList(根据定义应为空)。相反,当我获得代理并搜索树时,成千上万的无记录零记录查询。
我试过了: 1.使用左连接而不是连接 2.让孩子的列表不懒和fetch =“join”并删除hql(作为概念证明,性能下降在其他地方是不可接受的) 3.弄乱我看到有人在hibernate论坛中使用的未找到的属性
所有这些都给了我相同的结果......一个包含所有数据的大型查询(好)和数千个不返回数据的小查询(错误)。有什么想法吗?
我正在使用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>
任何帮助都将不胜感激!!
答案 0 :(得分:2)
我很久以前就对这个话题很感兴趣,所以请不要把我认为理所当然的事情当作理所当然的事(如果我错了,也许有更多NHibernate
经验的人可以纠正我。/ p>
上次我检查了描述的情况是一个很大的问题,因为Nhibernate
正在为每个level
个子实体生成单独的查询。如果你事先知道你的树的深度不会太大,那么你可能没有eager loading
孩子的生活...但如果你的结构没有深度限制,你应该考虑替代方案
假设您使用的是SqlServer
我发现很容易实现的一个解决方案是使用recursive self-join
/ CTE
。此解决方案涉及切换到stored-procedures
作为查询源并在代码中手动重新创建层次结构。要获取整个树,您只需要一个数据库查询(可以包含各种过滤器和子查询+排序),您仍然可以对所有NHibernate
操作重复使用CRUD
映射。此解决方案的一个缺点是您以后必须在db端维护查询代码(列和映射更改等)。
修改了预订树遍历算法
这是我的ideal
树持久性解决方案,因为它不需要任何stored-procedures
,并且可以与NHibernate
和Criteria API
完美集成(对我来说这是一个巨大的优势,因为我可以在代码中自由创建高级和可重用的过滤器)。从性能的角度来看,所有选择几乎都是免费的 - 您可以将平衡移向插入,更新和删除操作,因为您需要在每次更改时重新计算树 - 但这可以通过hql
来完成所以(伪代码):
HQLNamedQuery hql = new HQLNamedQuery();
hql.Query = "UPDATE " + typeof(THierarchy).FullName +
" SET TraversalLeft = (TraversalLeft + :traversalChange) " +
" WHERE BaseNodeId = :baseNodeId AND TraversalLeft > :minTr AND TraversalLeft <= :maxTr";
我已经设法使用这种技术实现各种操作(如添加,添加范围,移动,重新排序,获取后代,获得祖先,计算后代等),我必须说,一旦你得到了基本原则,你可以完美地将它整合到不同的项目中。
链接到让我开始讨论此主题的文章(遗憾的是它没有使用NHibernate
):
http://weblogs.asp.net/aghausman/archive/2009/03/16/storing-retrieving-hierarchical-data-in-sql-server-database.aspx
在NHibernate中映射树
也许您还应该看一下这篇文章(确切的部分从An alternative approach
开始):
http://nhibernate.hibernatingrhinos.com/16/how-to-map-a-tree-in-nhibernate
这基本上归结为向映射添加两个额外的集合:Ancestors
和Descendants
并执行三个查询:主查询,加载祖先和加载后代 - 这应该阻止NHibernate在执行时执行其他查询访问子节点。
我希望这会有所帮助。
答案 1 :(得分:0)
我认为根本原因是NHibernate无法知道查询返回层次结构中的所有记录。也就是说,它不知道节点是叶节点。怎么可能呢?
我的快速和肮脏的解决方案是向Proposal添加一个选择引导节点的方法,并用新的空集合替换代理集合。基本上你会通过调用这个方法通知Proposal它是完全填充的。