我是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 Key
的WHERE
子句生成新的查询。因此,这导致了著名的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