NHIBERNATE部队分开查询而不是加入



我是Nhibernate的新手,我无法弄清楚这一点。我有一个与班级以下的实体相似;

public class MotherCollection
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual ISet<Class1> Collection1 { get; set; }
    public virtual ISet<Class2> Collection2 { get; set; }
    public virtual ISet<Class3> Collection3 { get; set; }
    public virtual ISet<Class4> Collection4 { get; set; }
}

与其他实体有许多与许多关系的关系。我使用以下映射配置此关系;

HasMany(d => d.Collection1).KeyColumn("McId");
HasMany(d => d.Collection2).KeyColumn("McId");
HasMany(d => d.Collection3).KeyColumn("McId");
HasMany(d => d.Collection4).KeyColumn("McId");

儿童课程被配置为象征;

References(c1=>c1.MotherCollection).Column("McId");

等等。

当我从数据库中查询这个实体,获取所有关系时,我得到了一个与此相似的巨大查询:

SELECT * FROM MotherCollection mc 
JOIN c1 on mc.Id=c1.mcId
JOIN c2 on mc.Id=c2.mcId
JOIN c3 on mc.Id=c3.mcId
JOIN c4 on mc.Id=c4.mcId

此查询会导致很多重复行,并需要大量时间执行。

我希望nhibernate以某种方式将此查询分开至单个选择的查询,例如以下

SELECT * FROM MotherCollection Where Id = @Id
SELECT * FROM c1 Where mcId = @Id

等。当收藏集加载时,它的发生方式有点相似。我设法通过将所需的收藏设置为懒惰来实现此行为,并在退出数据层之前访问其第一个()属性。但是,我猜想必须有一种更优雅的方式来做到这一点。

我尝试了与此类似的查询:

var data = session.QueryOver<DataSet>().Fetch(d=>d.Collection1).Eager.Fetch(d=>d.Collection2).Eager....

谢谢。

您应该发出4个单独的查询,每个查询都取一个集合。您应该使用session.Query。查询是一种较旧的方法。要使用它添加using NHibernate.Linq。我通常使用以下扩展方法预取收集:

static public void Prefetch<T>(this IQueryable<T> query)
{
    // ReSharper disable once ReturnValueOfPureMethodIsNotUsed
    query.AsEnumerable().FirstOrDefault();
}

然后使用:

var data = session.Query<DataSet>().Fetch(d=>d.Collection1).ToList();
session.Query<DataSet>().Fetch(d=>d.Collection2).Prefetch();
session.Query<DataSet>().Fetch(d=>d.Collection3).Prefetch();
session.Query<DataSet>().Fetch(d=>d.Collection4).Prefetch();

在访问集合之前,请确保运行4个查询。这样,当您访问它们时,它们都将被初始化。如果您使用常规的懒惰加载,您将一次初始化一个对象的一个集合。

这称为懒惰/急切加载。您有两个选择要选择:

1。带有多个查询的懒负载:

这将生成多个查询。在懒惰加载时,NHIBERNATE首先生成查询以获取所有依赖表的所有MotherCollection数据和仅IDS(无数据)。然后,它在依赖表上使用Primary KeyWHERE子句生成新的查询。因此,这导致了著名的n 1期。

这样,默认情况下将不会填写引用的集合。当您第一次访问ISession时,这些将被填写。这类似于您在问题中提到的调用First()

查看您的HasMany配置;您尚未提及LazyLoad,但默认情况下。因此,有了您当前的映射,这就是正在发生的事情。

这是由Nhibernate推荐的。

2。带有单个复杂查询的急切负载:

如果要避免多个查询并一次检索所有数据,请尝试以下内容:

HasMany(d => d.Collection1).KeyColumn("McId").Inverse().Not.LazyLoad().Fetch.Join();

这样,将自动填写引用的集合(如果数据库中存在)。

请注意,这是针对NHIBERNATE建议的。请参阅此链接。

相反,我们保留默认行为,并覆盖它 特定交易,使用HQL中的left join fetch。这说明了 nhibrenate在第一个选择中急切地获取关联,使用 外部连接。在ICriteria查询API中,您将使用 SetFetchMode(FetchMode.Join)

如果您觉得自己希望可以改变获取策略 Get()Load()使用,只需使用ICriteria查询 示例:

User user = (User) session.CreateCriteria<User>()
                .SetFetchMode("Permissions", FetchMode.Join)
                .Add( Expression.Eq("Id", userId) )
                .UniqueResult();

避免n 1选择问题的一种完全不同的方法是 使用第二级缓存。

重复行和性能

这实际上是一个不同的问题。有多种处理方法;但是它将需要您的其他输入。在此之前,您应该从上面的两个选项中选择一个选项。因此,这值得一个新问题。

请参阅此答案:https://stackoverflow.com/a/30748639/5779732

最新更新