1

好的,我对这个 NHibernate 查询有点困惑。困惑在于 PasswordResetToken。

首先,这是映射:

public ContactMap()
    {
        Table("Contact");
        Id(x => x.ContactId, "ContactId").Unique().GeneratedBy.Increment();
        Map(x => x.EmailAddress);
        ...
        Map(x => x.JobTitle);

        References(x => x.PasswordResetToken, "EmailAddress")
            .PropertyRef(x => x.EmailAddress)
            .Cascade.None()
            .Not.LazyLoad()
            .Not.Update();


        HasMany(x => x.Roles)
            .Table("tblContactRole").KeyColumn("ContactId").Element("Role", part => part.Type<global::NHibernate.Type.EnumStringType<ContactRoles>>())
            .AsSet()
            .Not.LazyLoad();
    }

现在这里是查询:

public IList<Contact> GetContacts(int id)
    {
        var contacts = Session.CreateCriteria<Contact>()
                           .Add(Restrictions.Eq("Id", id))
                           .Add(Restrictions.Eq("IsActive", true))
                           .SetFetchMode("Roles", FetchMode.Eager)
                           .SetFetchMode("PasswordResetToken", FetchMode.Eager)
                           .SetResultTransformer(CriteriaSpecification.DistinctRootEntity)
                           .List<Contact>();

        return contacts;
    }

我的理解是 FetchMode.Eager 意味着使用 JOIN 而不是 SUBSELECT,因此没有任何理由出现对 db 的额外调用。

运行正确的 SQL 查询,返回为联系人补充所需的所有信息,如 NHProf 的屏幕截图所示(突出显示的查询)(不要担心不同的表名等 - 我已经清理了上面的代码):

NHProf

我不明白为什么会生成并运行对 PasswordResetToken 表的数十个单独选择?其中一个查询仅针对没有 PasswordResetToken 的每个联系人生成(即第一个查询为这些列返回空值) - 不确定这与它有什么关系。

一个联系人可能有也可能没有几个角色(对于这个问题是多余的),同样,可能有也可能没有一个 PasswordResetToken。

数据库有点狡猾,外键很少。在这种情况下,Contact 和 PasswordResetToken 之间的链接是一个简单的共享列“EmailAddress”。

所有这些查询都是在运行上面的那一行代码时生成的(即,该代码不在循环中)。

如果我遗漏任何信息,请告诉我。

我应该在谷歌上搜索什么?

4

1 回答 1

3

这是一个错误。我会尝试让它只使用两个查询,尽管从错误报告中听起来这将是一个挑战。

附加的测试表明,引用唯一属性(而不是 Id)的多对一关联会导致选择 n+1 问题。虽然第一条语句包含了正确的连接,但是所有关联的实体都是在连接选择之后一个接一个地取出来的。(在唯一列中具有相同值的实体甚至被多次获取。)

有趣的一点是,仅当引用的实体已经在会话缓存中时才会出现此错误。如果不是,则不会创建其他选择语句。

于 2013-03-20T16:00:21.000 回答