如何阻止NHibernate阅读儿童收藏?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何阻止NHibernate阅读儿童收藏?相关的知识,希望对你有一定的参考价值。

我的数据库中有文档列表。每个文档都有一个子元素集合。现在,我的方案是: - 读取文档(仅来自文档表) - 关闭会话并让用户做一些工作。

但是当我这样做时,Document会尝试在某些地方加载子元素。我不想要它。我想明确地阅读子元素。对于第一部分,我需要阅读简单的文档值。

那么有什么方法可以对nHibernate说 - “嘿,从来没有读过这个集合!”?

答案

将你的集合的懒惰加载设置为LazyExtra,也许你的设置为NoLazy(a.k.a.eager-loading)。

最好将它设置为Extra而不是Lazy。因为当你只想得到子集合的.Count().Any()时,它会阻止NHibernate获取子集合的行。 Extra就像懒惰的懒惰版本:)

使用NoLazy / eager-loading:

var post = session.Get<Post>(1);

即使您没有从应用程序访问帖子的子集合注释,也会从数据库的comments表中读取post表和行中的一行。

使用Lazyvar 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()只在应用程序端发生。

使用Extravar commentsCount = post.Comments.Count(),NHibernate只会发出计数查询,而不是读取所有行。

select count(*) from comments where post_id = 1

这是一个设置子集合加载机制的示例配置,如果您使用NHibernate的自动化,则在BeforeMapSet事件上设置该设置:

enter image description here

当您需要急切加载配置为LazyExtra的子集合时,请使用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阅读儿童收藏?的主要内容,如果未能解决你的问题,请参考以下文章

儿童片段到父母片段沟通

nhibernate教程--条件查询(Criteria Query)

如何阻止片段一直弹出到根片段? [导航组件]

JS常用代码片段2-值得收藏

JS常用代码片段2-值得收藏

使用 Fluent NHibernate 映射泛型类