0

我需要填充一些树层次结构并遍历它们以建立类别菜单。每个类别可以有超过 1 个父级。问题是如何有效地做到这一点,并尽量避免这个Select N+1问题。

目前,它是通过使用两个表/实体来实现的:

Category
--------
ID
Title

CategoryLink
---------
ID
CategoryID
ParentID

理想情况下,我会使用普通的对象遍历来遍历节点,即通过Category.ChildCategories等。这可以在一个 SQL 语句中完成吗?而且,这可以在 NHibernate 中完成吗?

4

2 回答 2

0

batch-sizeCategory.ChildCategories映射上指定 a 。这将导致 NHibernate 分批获取指定大小的子节点,而不是一次一个(这将缓解 N+1 问题)。

如果您使用的是.hbm文件,则可以batch-size这样指定:

<bag name="ChildCategories" batch-size="30">

或使用流利的映射

HasMany(x => x.ChildCategories).KeyColumn("ParentId").BatchSize(30);

有关详细信息,请参阅NHibernate 文档

编辑

好的,我相信我理解您的要求。使用以下配置

HasManyToMany<Item>(x => x.ChildCategories)
    .Table("CategoryLink")
    .ParentKeyColumn("ParentId")
    .ChildKeyColumn("CategoryID")
    .BatchSize(100)
    .Not.LazyLoad()
    .Fetch.Join();

您应该能够使用以下行在一次调用中获取整个层次结构。

var result = session.CreateCriteria(typeof(Category)).List();

出于某种原因,检索这样的单个类别

var categoryId = 1;
var result = session.Get<Category>(categoryId);

导致层次结构中的每一级调用一次。我相信这仍会显着减少对数据库的调用次数,但我无法让上面的示例与对数据库的单个调用一起工作。

于 2012-07-04T16:38:16.787 回答
0

这将检索所有类别及其子类别:

var result = session.Query<Category>()
                    .FetchMany(x => x.ChildCategories)
                    .ToList();

问题是确定什么是根类别。您可以使用标志,也可以映射逆向集合 ( ParentCategories) 并执行以下操作:

var root = session.Query<Category>()
                  .FetchMany(x => x.ChildCategories)
                  .FetchMany(x => x.ParentCategories)
                  .ToList()
                  .Where(x => !x.ParentCategories.Any());

所有排序都应在客户端完成(即之后ToList

于 2012-07-06T16:43:21.880 回答