NHibernate 父级、子级集合以及 future
我有这样的设置:父级,带有子级的集合。
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
I have this setup: Parent, with a collection of Child.
class Parent {
IList<Child> Childs { get; set; }
}
HQL:
("From Parent").Future();
("From Child").Future();
foreach(Parent p in result) {
foreach(Child c in p.Childs) {
}
}
This gives the classic N+1 problem. Two SQL statements are sent to server in 1 roundtrip, so all the data exists in first level cache, so why does NH still exists SQL for every child.
Version 3.1.0.400
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
当您执行将来的查询时,会将所有父对象和子对象拉入第一级缓存。 Parent 对象包含一个需要填充的惰性集合。为了填充集合,NHibernate 必须查询数据库。 (我们很快就会明白为什么。)查询返回子对象,并且这些子对象已经在一级缓存中。因此,这些对象用于填充集合。
现在为什么 NHibernate 必须查询数据库来填充 Childs 集合?您可以在集合上有一个“where”子句,用于过滤掉 IsDeleted==true 的子对象。您可以在 EventListener 中包含代码来过滤掉某些子对象。基本上,可能会发生很多事情,NHibernate 无法对 Parent 和 Child 对象之间的关系做出任何假设。
您可以通过在 HQL 或映射中指定获取策略来为其提供足够的信息。在 HQL 中,您可以这样写:
当您使用父对象获取子对象时,使用 future 的子对象查询将是完全可选的。由于连接获取,您将获得重复的父对象,尽管它们是相同的对象。 (您正在数据库中进行内部联接,并为每个子行返回父行的一份副本。)您可以通过迭代parents.Distinct() 来摆脱这些。
如果您始终想要获取具有相应父级的子对象,您还可以在父级映射中使用 fetch="join"。
如果这些选项都不适合您的场景,您可以在集合映射上指定批量大小。当您点击parent.Childs时,您仍然会执行数据库查询,但NHibernate将立即初始化任何其他集合代理。
When you execute the future query, you pull all Parent and Child objects into the 1st-level cache. The Parent objects contain a lazy collection, which needs to be populated. To populate the collection, NHibernate has to query the database. (We'll get to why in just a second.) The query returns Child objects and those child objects are already in the L1 cache. So those objects are used to populate the collection.
Now why does NHibernate have to query the database to populate the Childs collection? You could have a "where" clause on the collection that filters out Child objects with IsDeleted==true. You could have code in an EventListener that filters out certain Child objects. Basically there is a lot that can happen and NHibernate can't make any assumptions about the relationship between Parent and Child objects.
You can give it enough information by specifying a fetching strategy in the HQL or in your mapping. In HQL, you could write:
The Child object query using the future would be completely optional as you're fetching the children with the parents. Because of the join fetch, you will get duplicate Parent objects, though they'll be the same object. (You're doing an inner join in the database and returning one copy of the parent row for each child row.) You can get rid of these by iterating over parents.Distinct().
If you always want to fetch Child objects with the corresponding Parent, you can also use fetch="join" in your Parent mapping.
If neither of these options works for your scenario, you can specify batch-size on the collection mapping. You will still execute a database query when you hit parent.Childs, but NHibernate will eagerly initialize any other collection proxies.