复杂对象的Fluent NHibernate N+1问题



我有一个问题与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);
    }
}

方法是使用批处理抓取。点击这里阅读更多内容:

如何在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()进行关联。但我不建议这样做,因为它会导致简单的查询变得非常庞大。批处理或子查询也可以帮助。

最新更新