如何阻止NHibernate阅读儿童收藏?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何阻止NHibernate阅读儿童收藏?相关的知识,希望对你有一定的参考价值。
我的数据库中有文档列表。每个文档都有一个子元素集合。现在,我的方案是: - 读取文档(仅来自文档表) - 关闭会话并让用户做一些工作。
但是当我这样做时,Document会尝试在某些地方加载子元素。我不想要它。我想明确地阅读子元素。对于第一部分,我需要阅读简单的文档值。
那么有什么方法可以对nHibernate说 - “嘿,从来没有读过这个集合!”?
将你的集合的懒惰加载设置为Lazy
或Extra
,也许你的设置为NoLazy
(a.k.a.eager-loading)。
最好将它设置为Extra
而不是Lazy
。因为当你只想得到子集合的.Count()
或.Any()
时,它会阻止NHibernate获取子集合的行。 Extra
就像懒惰的懒惰版本:)
使用NoLazy / eager-loading:
var post = session.Get<Post>(1);
即使您没有从应用程序访问帖子的子集合注释,也会从数据库的comments表中读取post表和行中的一行。
使用Lazy
,var post = session.Get<Post>(1)
只会从posts表中加载一行,NHibernate不会从数据库中读取帖子的子集合注释。
As for the Lazy vs Extra
使用Lazy:
var commentsCount = post.Comments.Count()
这将从数据库加载帖子的评论:
select * from comments where post_id = 1;
.Count()
只在应用程序端发生。
使用Extra,var commentsCount = post.Comments.Count()
,NHibernate只会发出计数查询,而不是读取所有行。
select count(*) from comments where post_id = 1
这是一个设置子集合加载机制的示例配置,如果您使用NHibernate的自动化,则在BeforeMapSet事件上设置该设置:
当您需要急切加载配置为Lazy
或Extra
的子集合时,请使用FetchMany
作为临时解决方案,我创建了一个简单的hack:
public class Document
{
IList<Periods> periods;
public virtual IList<Period> Periods
{
get { return periods; }
set { periods = value; }
}
public virtual void ResetPeriods()
{
periods = new List<Period>();
}
}
这就是我获取文件的方式:
db.BeginTransaction();
IList<Document> list = db.Get<Document>();
db.CommitTransaction();
List<Document> result = new List<Document>();
foreach (var item in list)
{
item.ResetPeriods(); //todo: HACK! Preventing from lazy load of periods
result.Add(item);
}
return result;
当然这个集合被映射为懒惰。子集合(Periods)必须定义为后变量,因为它阻止NHibernate Proxy使用属性getter。
我找到了导致文档的期间从数据库加载的原因,即使您没有访问Document的Periods属性。
namespace NHibernateFetchJoinTest2
{
using System;
using NHibernateFetchJoinTest2.DomainMapping;
using NHibernateFetchJoinTest2.Domains;
class MainClass
{
public static void Main(string[] args)
{
using (var session = Mapper.SessionFactory.OpenSession())
{
Console.WriteLine("SQL produced: ");
var d = session.Get<Document>(1);
Console.ReadLine();
//Console.WriteLine("Document's periods: ");
//foreach (var period in d.Periods)
//{
// Console.WriteLine($"* {period.PeriodDescription}");
//}
Console.ReadLine();
}
}
}
}
产生这个:
SQL produced:
NHibernate:
SELECT
document0_.Id as id1_0_1_,
document0_.DocumentDescription as documentdescription2_0_1_,
periods1_.DocumentId as documentid3_1_3_,
periods1_.Id as id1_1_3_,
periods1_.Id as id1_1_0_,
periods1_.PeriodDescription as perioddescription2_1_0_
FROM
Document document0_
left outer join
Period periods1_
on document0_.Id=periods1_.DocumentId
WHERE
document0_.Id=@p0;
@p0 = 1 [Type: Int32 (0:0:0)]
您的映射类似于以下内容。你的孩子集合Lazy
-loading设置为Lazy
(而不是NoLazy
),但它的Fetch
策略设置为Join
。以机智:
namespace NHibernateFetchJoinTest2.DomainMapping.Mappings
{
using NHibernate.Mapping.ByCode.Conformist;
using NHibernateFetchJoinTest2.Domains;
public class DocumentMapping : ClassMapping<Document>
{
public DocumentMapping()
{
Id(x => x.Id);
Property(x => x.DocumentDescription);
Bag(x => x.Periods, collectionMapping =>
{
collectionMapping.Inverse(true);
collectionMapping.Key(k => k.Column("DocumentId"));
collectionMapping.Lazy(NHibernate.Mapping.ByCode.CollectionLazy.Lazy);
// Remove this. This causes Document's Periods to load,
// even if child collection Periods is not accessed yet.
// This is evident in SQL log, it shows LEFT JOIN Period.
collectionMapping.Fetch(NHibernate.Mapping.ByCode.CollectionFetchMode.Join);
}, mapping => mapping.OneToMany());
}
}
public class PeriodMapping: ClassMapping<Period>
{
public PeriodMapping()
{
Id(x => x.Id);
Property(x => x.PeriodDescription);
}
}
}
如果这被删除......
collectionMapping.Fetch(NHibernate.Mapping.ByCode.CollectionFetchMode.Join);
...子集合期间不是由其父级(Document)过早提取的:
SQL produced:
NHibernate:
SELECT
document0_.Id as id1_0_0_,
document0_.DocumentDescription as documentdescription2_0_0_
FROM
Document document0_
WHERE
document0_.Id=@p0;
@p0 = 1 [Type: Int32 (0:0:0)]
使用的Repro步骤:https://github.com/MichaelBuen/NHibernateFetchJoinTest2
以上是关于如何阻止NHibernate阅读儿童收藏?的主要内容,如果未能解决你的问题,请参考以下文章