Skip to content

Files

Latest commit

fe85a71 · Jan 9, 2022

History

History
294 lines (231 loc) · 8.71 KB

File metadata and controls

294 lines (231 loc) · 8.71 KB

七、限制和过滤器

限制

可以为实体指定附加的限制条件,NHibernate 在执行 SELECTs 时会始终遵守这些条件。这些条件有两种:限制和过滤。让我们首先关注限制。

限制是一个静态 WHERE 子句,可以在类和集合级别(集合列表映射id 包数组原始数组)添加,以过滤其元素。一个典型的例子是软删除,其中记录实际上并没有从数据库中删除,而是用表示删除的列值来标记。下面是我们如何为类和集合将它定义为 XML:

            <class name="Blog" lazy="true" table="`POST`" where="deleted = 0">
              <!-- ... -->
              <list cascade="all-delete-orphan" inverse="true" lazy="true" name="Posts" where="deleted = 0">
                <!-- ... -->
              </list>
            </class>

下面是代码映射中的等效代码:

            mapper.Class<Blog>(ca =>
            {
              ca.Where("deleted = 0");
              //...

              ca.List(c => c.Posts, c =>
              {
                c.Where("deleted = 0");
                //...
              });
            }

在属性中:

            [Class(Table = "blog", Lazy = true, Where = "deleted = 0")]
            public class Blog
            {
            //...
            [List(0, Inverse = true, Lazy = CollectionLazy.True, Generic = true, Where = "deleted = 0", Cascade = "all-delete-orphan")]
              public virtual IList<Post> Posts { get; protected set; }
            }

deleted = 0”限制:

            IEnumerable<Blog> nonDeletedBlogs = session.Query<Blog>();
            Blog b = session.Get<Blog>(1);

查看两个查询的结果 SQL:

            SELECT
            blog0_.blog_id AS blog1_11_,
            blog0_.picture AS picture11_,
            blog0_.user_id AS user3_11_,
            blog0_.name AS name11_,
            blog0_.creation AS creation11_,
            (SELECT
            COUNT(1)
            FROM
            post
            WHERE
            post.blog_id = blog0_.blog_id) AS formula2_
            FROM
                blog blog0_
            WHERE
            (
            blog0_.deleted = 0
                )

            SELECT
            blog0_.blog_id AS blog1_11_0_,
            blog0_.picture AS picture11_0_,
            blog0_.user_id AS user3_11_0_,
            blog0_.name AS name11_0_,
            blog0_.creation AS creation11_0_,
            (SELECT
            COUNT(1)
            FROM
            post
            WHERE
            post.blog_id = blog0_.blog_id) AS formula2_0_
            FROM
            blog blog0_
            WHERE
            blog0_.blog_id = 1
            AND
            xml:lang="PT">(
            blog0_.deleted = 0
                )

过滤器

过滤器类似于限制,但它是动态的。这意味着它可以启用或禁用,并且可以有参数。一个例子可能是您将术语翻译成多种语言的模型:

图 25:翻译类模型

我们所拥有的是:

  • 一种语言
  • 一个术语和一组翻译
  • 给定语言的术语的翻译

这引入了一个新概念:映射复合键的类。在这种情况下,是语言术语包含了对术语语言的引用。

通常,如果您只想加载当前语言的翻译,而不是所有语言的翻译,您会使用这样的模型。它的映射可能如下所示:

            public class LanguageMapping : ClassMapping<Language>
            {
              public LanguageMapping()
              {
                this.Table("language");
                this.Lazy(true);

                this.Id(x => x.LanguageId, x =>
                {
                  x.Column("language_id");
                  x.Generator(Generators.Assigned);
                });

                this.Property(x => x.Name, x =>
                {
                  x.Column("name");
                  x.NotNullable(true);
                  x.Length(100);
                });
              }
            }
            public class TermMapping : ClassMapping<Term>
            {
              public TermMapping()
              {
                this.Table("term");
                this.Lazy(true);

                this.Id(x => x.TermId, x =>
                {
                  x.Column("term_id");
                  x.Generator(Generators.HighLow);
                });

                this.Property(x => x.Description, x =>
                {
                  x.Column("description");
                  x.NotNullable(true);
                  x.Length(50);
                });

                this.Set(x => x.Translations, x =>
                {
                  x.Key(y =>
                  {
                    y.Column("term_id");
                    y.NotNullable(true);
                  });

                  x.Filter("CurrentLanguage", z =>
                  {
                      z.Condition("language_id = :code");
                  });

                  x.Inverse(true);
                  x.Cascade(Cascade.All | Cascade.DeleteOrphans);
                  x.Lazy(CollectionLazy.Lazy);
                }, x =>
                {
                  x.OneToMany();
                });
              }
            }
            public class TranslationMapping : ClassMapping<Translation>
            {
              public TranslationMapping()
              {
                this.Table("translation");
                this.Lazy(true);

                this.Filter("CurrentLanguage", x =>
                {
                    x.Condition("language_id = :code");
                });

                this.ComponentAsId(x => x.TranslationId, x =>
                {
                  x.ManyToOne(y => y.Language, y =>
                  {
                    y.Column("language_id");
                  });
                  x.ManyToOne(y => y.Term, y =>
                  {
                    y.Column("term_id");
                  });
                });

                this.Property(x => x.Text, x =>
                {
                  x.Column("text");
                  x.Length(100);
                  x.NotNullable(true);
                });
              }
            }

最后但并非最不重要的是,在会话工厂构建之前,必须在配置实例上进行过滤器声明:

            cfg.AddFilterDefinition(new FilterDefinition("CurrentLanguage", "language_id = :code", 
            new Dictionary<String, IType>() { { "code", NHibernateUtil.String } }, false));

| | 提示:导入命名空间 NHibernate.Engine。 |

请注意:

  • CurrentLanguage 过滤器基本上是对 language_id 列的限制,它使用了一个代码参数。
  • 语言类的主键语言标识,是一个字符串,使用指定的模式。我们将使用它来存储有意义的文化名称,如“ en-us ”或“ pt-pt ”,它们必须始终是唯一的。
  • 术语类的翻译集合应用了当前语言过滤器。
  • 翻译类有一个由语言术语类实现的复合键(组件,它引用了一个术语和一个语言
  • 翻译类也使用货币语言过滤器。

我们需要给过滤器的代码参数赋值,并在实际查询前启用它:

            //set the filter value from the current thread’s culture name
            session.EnableFilter("CurrentLanguage").SetParameter("code", Thread.CurrentThread.CurrentCulture.Name);

            var term = session.Query<Term>().First();

            >//the translations will be filtered
            var translations = term.Translations.ToList();

过滤器将作为参数包含在 SQL 中:

            SELECT
            TOP (1) term0_.term_id AS term1_8_,
            term0_.description AS descript2_8_
            FROM
            term term0_

            SELECT
            translatio0_.term_id AS term2_1_,
            translatio0_.language_id AS language1_1_,
            translatio0_.language_id AS language1_9_0_,
            translatio0_.term_id AS term2_9_0_,
            translatio0_.text AS text9_0_
            FROM
            [translation] translatio0_
            WHERE
            translatio0_.language_id = 'en-us'
                AND translatio0_.term_id = 1

当您不再需要过滤器时,只需将其禁用:

            //disable the filter
            session.DisableFilter("CurrentLanguage");

最终注释:

  • 每个实体和集合可能有多个筛选器,并且可以同时启用多个筛选器。
  • 过滤器可以有参数,也可以没有参数,这些参数可以有默认值。
  • 可以启用或禁用过滤器。
  • 筛选器的 SQL 只能引用应用它的类可直接访问的列。