How to block NHibernate from reading child collection?
我的数据库里有文件清单。每个文档都有一个子元素集合。现在,我的设想是:-读取文档(仅从文档表)-关闭会话并让用户做一些工作。
但当我这样做时,文档试图在某些地方加载子元素。我不想要它。我只想明确地阅读儿童元素。对于第一部分,我只需要阅读简单的文档值。
所以有什么办法可以对NHibernate说:"嘿,不要读这本书!"?
将集合的延迟加载设置为
最好是设置为
带着Nolazy/热切的装载:
1 | var post = session.Get<Post>(1); |
这将从发布表中读取一行,从数据库中读取评论表中的行,即使您没有从应用程序访问发布的子集合评论。
使用
使用懒惰:
1 | var commentsCount = post.Comments.Count() |
这将从数据库中加载日志的注释:
1 | select * from comments where post_id = 1; |
而
使用extra,
1 | select count(*) from comments where post_id = 1 |
下面是一个设置子集合加载机制的配置示例。如果使用nHibernate的自动应用,则在BeforeMapset事件上设置该设置:
当需要预先加载配置为
我发现了导致从数据库加载文档周期的原因,即使您不访问文档的Periods属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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(); } } } } |
产生这种情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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)] |
您的映射如下所示。您的子集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | 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); } } } |
如果删除此项…
1 | collectionMapping.Fetch(NHibernate.Mapping.ByCode.CollectionFetchMode.Join); |
…子集合期间不是由其父级(文档)过早提取的:
1 2 3 4 5 6 7 8 9 10 | 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)] |
重复使用的步骤:https://github.com/michaelbuen/nhibernatefectjointest2
作为临时解决方案,我创建了一个简单的黑客程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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>(); } } |
我就是这样得到文件的:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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; |
当然,这个集合被映射为lazy。子集合(句点)必须定义为back变量,因为它会阻止nhibernate代理使用属性getter。