好的,我对这个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,因此没有任何理由可以对数据库进行额外调用。
运行正确的SQL查询,返回水合联系所需的所有信息,如NHProf的屏幕截图所示(突出显示的查询)(不要担心不同的表名等 - 我已经清理了上面的代码):
我不明白为什么在地球上生成并运行几十个单独的选择到PasswordResetToken表?其中一个查询仅为没有PasswordResetToken的每个联系人生成(即第一个查询为这些列返回空值) - 不确定这与它有什么关系。
联系人可能有也可能没有几个角色(这个问题多余),同样,可能有也可能没有一个PasswordResetToken。
数据库有点狡猾,几乎没有外键。在这种情况下,Contact和PasswordResetToken之间的链接是一个简单的共享列“EmailAddress”。
所有这些查询都是在上面那一行代码的运行时生成的(即代码不在循环中)。
如果我遗漏任何信息,请告诉我。
我应该用谷歌搜索什么?
答案 0 :(得分:3)
这是一个bug。我会试着让它只用两个查询,虽然从错误报告中听起来这将是一个挑战。
附加测试表明,引用唯一属性(而不是Id)的多对一关联会导致选择n + 1问题。虽然第一个语句包含正确的连接,但是在连接选择之后逐个提取所有关联的实体。 (唯一列中具有相同值的实体甚至被多次提取。)
有趣的是,只有引用时才会出现此错误 实体已经在会话缓存中。如果他们不是,不 创建了其他选择语句。