复杂对象的 Fluent NHibernate N+1 问题
Fluent NHibernate N+1 issue with complex objects
我遇到了 NHibernate 查询数据库次数过多的问题。我刚刚意识到它可能与 n+1 问题有关,但我不知道如何更改我的映射来解决问题。
正如您将看到的,我的尝试包括指定不延迟加载其他对象,但它似乎没有成功。
这是查询:
public IQueryable<Report> ReadAll(DateTime since)
{
return m_session.QueryOver<Report>()
.JoinQueryOver(r => r.Mail)
.Where(m => m.Received >= since)
.List()
.AsQueryable();
}
提前感谢您的回复!如果您需要有关我的对象或映射的更多信息,请告诉我。
简化对象图(部分省略):
public class Report : EntityBase
{
public virtual Product Product { get; set; }
public virtual StackTrace StackTrace { get; set; }
public virtual Mail Mail { get; set; }
public virtual IList<ClientUser> ReadBy { get; set; }
}
-
public class Product : EntityBase
{
public virtual string Name { get; set; }
public virtual Version Version { get; set; }
public virtual IList<Report> Reports { get; set; }
public virtual IList<StackTrace> StackTraces { get; set; }
}
-
public class StackTrace : EntityBase
{
public virtual IList<StackTraceEntry> Entries { get; set; }
public virtual IList<Report> Reports { get; set; }
public virtual Product Product { get; set; }
}
映射示例:
public class ReportMap : ClassMap<Report>
{
public ReportMap()
{
Table("Report");
References(x => x.User)
.Column("EndUserId")
.Not.LazyLoad();
References(x => x.Product)
.Column("ProductId")
.Not.LazyLoad();
References(x => x.StackTrace)
.Column("StackTraceId")
.Not.LazyLoad();
HasManyToMany(x => x.ReadBy)
.Cascade.SaveUpdate()
.Table("ClientUserRead")
.ParentKeyColumn("ReportId")
.ChildKeyColumn("ClientUserId")
.Not.LazyLoad().BatchSize(200);
}
}
-
public class StackTraceMap : ClassMap<StackTrace>
{
public StackTraceMap()
{
Table("StackTrace");
References(x => x.Product)
.Column("ProductId");
HasMany(x => x.Entries)
.KeyColumn("StackTraceId")
.Not.LazyLoad()
.Cascade
.All().BatchSize(500);
HasMany(x => x.Reports)
.KeyColumn("StackTraceId")
.Inverse().BatchSize(100);
}
}
要走的路是使用 batch fetching。在这里阅读更多相关信息:
How to Eager Load Associations without duplication in NHibernate?
在每个实体映射上应用 BatchSize
(对于 many-to-one
关系 - 避免 1 + N)
public ReportMap()
{
Table(...)
BatchSize(25);
...
并且在每个集合中 (解决 one-to-many
1 + N 的问题)
HasMany(x => x.Reports)
.KeyColumn("StackTraceId")
.BatchSize(25)
...
您可以在查询中指定提取路径。例如,这个获取路径可以告诉查询急切加入产品和主要对象,以及对客户集合的假想关联:
public IQueryable<Report> ReadAll(DateTime since)
{
return m_session.QueryOver<Report>()
.JoinQueryOver(r => r.Mail)
.Where(m => m.Received >= since)
.Fetch(x => x.Product).Eager
.Fetch(x => x.Mail).Eager
.Fetch(x => x.ReadBy.First().SomeProp).Eager
.List()
.AsQueryable();
}
如果您希望这种情况始终发生,请尝试使用 .Fetch.Join()
而不是 .Not.LazyLoad()
作为关联。但我不建议这样做,因为它会导致简单的查询变得庞大。批处理或子查询也有帮助。
我遇到了 NHibernate 查询数据库次数过多的问题。我刚刚意识到它可能与 n+1 问题有关,但我不知道如何更改我的映射来解决问题。
正如您将看到的,我的尝试包括指定不延迟加载其他对象,但它似乎没有成功。
这是查询:
public IQueryable<Report> ReadAll(DateTime since)
{
return m_session.QueryOver<Report>()
.JoinQueryOver(r => r.Mail)
.Where(m => m.Received >= since)
.List()
.AsQueryable();
}
提前感谢您的回复!如果您需要有关我的对象或映射的更多信息,请告诉我。
简化对象图(部分省略):
public class Report : EntityBase
{
public virtual Product Product { get; set; }
public virtual StackTrace StackTrace { get; set; }
public virtual Mail Mail { get; set; }
public virtual IList<ClientUser> ReadBy { get; set; }
}
-
public class Product : EntityBase
{
public virtual string Name { get; set; }
public virtual Version Version { get; set; }
public virtual IList<Report> Reports { get; set; }
public virtual IList<StackTrace> StackTraces { get; set; }
}
-
public class StackTrace : EntityBase
{
public virtual IList<StackTraceEntry> Entries { get; set; }
public virtual IList<Report> Reports { get; set; }
public virtual Product Product { get; set; }
}
映射示例:
public class ReportMap : ClassMap<Report>
{
public ReportMap()
{
Table("Report");
References(x => x.User)
.Column("EndUserId")
.Not.LazyLoad();
References(x => x.Product)
.Column("ProductId")
.Not.LazyLoad();
References(x => x.StackTrace)
.Column("StackTraceId")
.Not.LazyLoad();
HasManyToMany(x => x.ReadBy)
.Cascade.SaveUpdate()
.Table("ClientUserRead")
.ParentKeyColumn("ReportId")
.ChildKeyColumn("ClientUserId")
.Not.LazyLoad().BatchSize(200);
}
}
-
public class StackTraceMap : ClassMap<StackTrace>
{
public StackTraceMap()
{
Table("StackTrace");
References(x => x.Product)
.Column("ProductId");
HasMany(x => x.Entries)
.KeyColumn("StackTraceId")
.Not.LazyLoad()
.Cascade
.All().BatchSize(500);
HasMany(x => x.Reports)
.KeyColumn("StackTraceId")
.Inverse().BatchSize(100);
}
}
要走的路是使用 batch fetching。在这里阅读更多相关信息:
How to Eager Load Associations without duplication in NHibernate?
在每个实体映射上应用 BatchSize
(对于 many-to-one
关系 - 避免 1 + N)
public ReportMap()
{
Table(...)
BatchSize(25);
...
并且在每个集合中 (解决 one-to-many
1 + N 的问题)
HasMany(x => x.Reports)
.KeyColumn("StackTraceId")
.BatchSize(25)
...
您可以在查询中指定提取路径。例如,这个获取路径可以告诉查询急切加入产品和主要对象,以及对客户集合的假想关联:
public IQueryable<Report> ReadAll(DateTime since)
{
return m_session.QueryOver<Report>()
.JoinQueryOver(r => r.Mail)
.Where(m => m.Received >= since)
.Fetch(x => x.Product).Eager
.Fetch(x => x.Mail).Eager
.Fetch(x => x.ReadBy.First().SomeProp).Eager
.List()
.AsQueryable();
}
如果您希望这种情况始终发生,请尝试使用 .Fetch.Join()
而不是 .Not.LazyLoad()
作为关联。但我不建议这样做,因为它会导致简单的查询变得庞大。批处理或子查询也有帮助。