• NHibernate二级缓存(第十一篇)


    一、NHibernate二级缓存简介

      NHibernate由ISessionFactory创建,可以被所有的ISession共享。   

      注意NHibernate查找缓存的顺序,在使用ISession操作数据时,NHibernate会先从一级缓存中查找需要的数据,如果一级 缓存不存在需要的数据,则查找二级缓存,如果二级缓存存在所需数据,则直接使用缓存中的数据。如果二级缓存都没有,那么才执行SQL语句,从数据库中查找 缓存。 NHibernate本身提供了一个基于Hashtable的HashtableCache缓存,不过功能有限且性能不高。不适合用于大型应用程序,不过 我们可以使用第三方缓存提供程序作为NHibernate二级缓存实现。
      启用二级缓存
      NHibernate默认是不开启二级缓存的,要开启NHibernate要在配置属性中配置如下三个属性:

    <!-- 配置二级缓存实现程序 -->
    <property name="cache.provider_class">NHibernate.Cache.HashtableCacheProvider</property>`
    <!-- 开启二级缓存 --> <property name="cache.use_second_level_cache">true</property>
    <!-- 在查询中开启二级缓存 -->` <property name="cache.use_query_cache">true</property>`

      NHibernate提供了六种第三方二级缓存提供程序。都是开源的。

    • NHibernate.Caches.MemCache
    • NHibernate.Caches.Prevalence
    • NHibernate.Caches.SharedCache
    • NHibernate.Caches.SysCache
    • NHibernate.Caches.SysCache2
    • NHibernate.Caches.Velocity

      缓存策略

      缓存策略可以在配置文件中指定,也可以在每一个映射文件中指定,建议尽量在配置文件中指定。这样不用兼顾那么多的映射文件。

      指定类:

    <class-cache class="类名称" region="默认类名称" include="all|non-lazy"
                 usage="read-only|read-write|nonstrict-read-write|transactional" />

      指定集合:

    <collection-cache collection ="集合名称" region="默认集合名称"
                      usage="read-only|read-write|nonstrict-read-write|transactional"/>
    • region:可选,默认值为类或集合的名称,用来指定二级缓存的区域名,对应于缓存实现的一个命名缓存区域。
    • include:可选,默认值为all,当取non-lazy时设置延迟加载的持久化实例的属性不被缓存。
    • usage:声明缓存同步策略,就是上面说明的四种缓存策略。

      读写缓存策略的说明:

    • read-only:只读缓存。适用于只读数据。可用于群集中。
    • read-write:读写缓存。
    • nonstrict-read-write:非严格读写缓存。不保证缓存与数据库的一致性。
    • transactional:事务缓存。提供可重复读的事务隔离级别。

      查询二级缓存配置:

    • Cacheable 为一个查询显示启用二级缓存;

    • CacheMode 缓存模式, 有如下可选:

      1. Ignore:更新数据时将二级缓存失效,其它时间不和二级缓存交互
      2. Put:向二级缓存写数据,但不从二级缓存读数据
      3. Get:从二级缓存读数据,仅在数据更新时向二级缓存写数据
      4. Normal:默认方式。从二级缓存读/写数据
      5. Refresh:向二级缓存写数据,想不从二级缓存读数据,通过在配置文件设置 cache.use_minimal_puts从数据库中读取数据时,强制二级缓存刷新
    • CacheRegion 给查询缓存指定了特定的命名缓存区域, 如果两个查询相同, 但是指定的 CacheRegion 不同, 则也会从数据库查询数据。

    二、NHibernate二级缓存的实现

      配置文件App.Config:

    复制代码
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <configSections>
        <section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
      </configSections>
      <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
        <session-factory>
          <property name="dialect">NHibernate.Dialect.MsSql2008Dialect</property>
          <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
          <property name="show_sql">true</property>
          <property name="connection.connection_string">
            Server=KISSDODOG-PC;initial catalog=Test;uid=sa;pwd=123;
          </property>
          <!-- 配置二级缓存实现程序 -->
          <property name="cache.provider_class">NHibernate.Cache.HashtableCacheProvider</property>
          <!-- 开启二级缓存 -->
          <property name="cache.use_second_level_cache">true</property>
          <!-- 在查询中开启二级缓存 -->
          <property name="cache.use_query_cache">true</property>
          <mapping assembly="Model" />
          <!-- 配置映射的二级缓存 -->
          <class-cache class="Model.PersonModel,Model" usage="read-write"/>
        </session-factory>
      </hibernate-configuration>
    </configuration>
    复制代码

      映射文件Person.hbm.xml

    复制代码
    <?xml version="1.0" encoding="utf-8" ?>
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
      <class name="Model.PersonModel,Model" table="Person">
        <!-- 配置缓存策略 -->
        <cache usage="read-write"/>
        <id name="Id" column="Id" type="Int32">
          <generator  class="native"/>
        </id>
        <property name="Name" column="Name" type="String"/>
      </class>
    </hibernate-mapping>
    复制代码

      Program.cs

    复制代码
        class Program
        {
            static void Main(string[] args)
            {
                ISessionFactory sessionFactory = new Configuration().Configure().BuildSessionFactory();
                ISession session = sessionFactory.OpenSession();
    
                IList<PersonModel> ListPerson1 = session.QueryOver<PersonModel>().Cacheable().CacheMode(CacheMode.Normal).CacheRegion("AllCategories").List();
                Console.WriteLine(ListPerson1[0].Name);
                IList<PersonModel> ListPerson2 = session.QueryOver<PersonModel>().Cacheable().CacheMode(CacheMode.Normal).CacheRegion("AllPerson").List();
                Console.WriteLine(ListPerson1[0].Name);
    
                Console.ReadKey();
            }
        }
    复制代码

      输出结果如下:

      

      我们来梳理下NHibernate的执行过程:

      首先,第一次查询,NHibernate查询了一级缓存,二级缓存都没有需要的数据,因此执行SQL语句,从数据库获得数据,返回所需对象。

      然后,第二次查询,NHibernate查询了一级缓存,发现没有数据,然后查询二级缓存,发现有数据,因此直接返回所需对象。

      缓存查询

      在NHibernate除了缓存持久化类和集合之外,查询得到的结果集也是可以缓存的。如果程序中经常使用同样的查询数据,则可以使用查询缓存。

      第一步,在配置文件中,启用查询缓存:

      <property name="cache.use_query_cache">true</property>

      启动了查询缓存之后,NHiberate将创建两个缓存区域。一个用于保存查询结果集,有NHibernate.Cache.StandardQueryCache实现。一个用来保存最近更新的查询表的时间戳,由NHibernate.Cache.UpdateTimeStampsCache实现。

      查询缓存中的集合会根据数据库的更改而随之改变。因此对大多数查询来说,查询缓存的益处不是很大,NHibernate在默认情况下不对查询进行缓存。

      如果需要对查询缓存,还要显式的使用IQuery.SetCacheable(true)方法。IQuery调用这个方法后,NHibernate将根据查询语句、查询参数、结果集其实范围等信息组成一个IQueryKey。接着根据这个IQueryKey到查询缓存中查找相应数据,查询成功则直接返回查找结果。但没有结果时才去数据查询,并放入查询缓存。如果IQueryKey数据发生改变,这些IQueryKey及其对象的结果集将从缓存中删除。

      Program.cs

    复制代码
        class Program
        {
            static void Main(string[] args)
            {
                ISessionFactory sessionFactory = new Configuration().Configure().BuildSessionFactory();
                
                using (ISession session = sessionFactory.OpenSession())
                {
                    Console.WriteLine("第一次查询---------------");
                    IList<PersonModel> ListPerson = session.QueryOver<PersonModel>().Where(p => p.Id > 1).Cacheable().CacheMode(CacheMode.Normal).CacheRegion("AllPerson").List();
                    foreach (var p in ListPerson)
                    {
                        Console.WriteLine(p.Name);
                    }
                }
    
                using (ISession session = sessionFactory.OpenSession())
                {
                    Console.WriteLine("第一次查询---------------");
                    IList<PersonModel> ListPerson = session.QueryOver<PersonModel>().Where(p => p.Id > 1).Cacheable().CacheMode(CacheMode.Normal).CacheRegion("AllPerson").List();
                    foreach (var p in ListPerson)
                    {
                        Console.WriteLine(p.Name);
                    }
                }
    
                Console.ReadKey();
            }
    复制代码

      输出结果如下:

      

      我们看到,第二次查询,并没有执行SQL语句,而是直接从缓存中返回数据。

      下面,我们来修改一下配置文件:

      <property name="cache.use_query_cache">false</property>

      再执行,看到结果如下:

      

      当我们关闭了,查询缓存之后,第二次查询的时候,也执行SQL语句,从数据库读取数据。

      缓存区域

      前面我们已经说过,当缓存区域不相同时,查询也不会使用二级缓存,而是会查询数据库。

      我们将第二条查询语句的缓存区域改成与第一条不同的(查询缓存是开启的):

    IList<PersonModel> ListPerson = session.QueryOver<PersonModel>().Cacheable().CacheMode(CacheMode.Normal).CacheRegion("AllPersonChange").List();

      执行结果如下:

      

      我们看到,更改了缓存区域之后,第二次查询也不会使用二级缓存,而是执行了SQL语句,从数据库获得返回数据。

      命名查询

      可以在映射文件中定义命名查询,<query>提供了很多属性可以用于缓存结果。

      映射文件Person.hbm.cs

    复制代码
    <?xml version="1.0" encoding="utf-8" ?>
    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
      <class name="Model.PersonModel,Model" table="Person">
        <!-- 配置缓存策略 -->
        <cache usage="read-write"/>
        <id name="Id" column="Id" type="Int32">
          <generator  class="native"/>
        </id>
        <property name="Name" column="Name" type="String"/>
      </class>
      <query cacheable="true" cache-mode="normal" name="GetAllPerson">
        from PersonModel
      </query>
    </hibernate-mapping>
    复制代码

      Program.cs

    复制代码
        class Program
        {
            static void Main(string[] args)
            {
                ISessionFactory sessionFactory = new Configuration().Configure().BuildSessionFactory();
    
                using (ISession session = sessionFactory.OpenSession())
                {
                    Console.WriteLine("--->第一次使用命名查询");
                    IList<PersonModel> PersonList1 = session.GetNamedQuery("GetAllPerson").List<PersonModel>();
                    Console.WriteLine(PersonList1[0].Name);
                }
                using (ISession session = sessionFactory.OpenSession())
                {
                    Console.WriteLine("--->第二次使用命名查询");
                    IList<PersonModel> PersonList2 = session.GetNamedQuery("GetAllPerson").List<PersonModel>();
                    Console.WriteLine(PersonList2[0].Name);
                }
    
                Console.ReadKey();
            }
        }
    复制代码

      输出结果如下:

      

    三、二级缓存的管理

      NHibernate二级缓存由ISessionFactory创建并由ISessionFactory自行维护。我们使用NHibernate操作数据时,ISessionFactory能够自动同步缓存,保证缓存的有效性。但我们批量操作数据时,NHibernate往往不能维护缓存持久有效。ISessionFactory提供了一系列方法供我们管理二级缓存。

      移除二级缓存的一系方法

    • Evict(persistentClass):从二级缓存中删除persistentClass类所有实例
    • Evict(persistentClass, id):从二级缓存中删除指定的持久化实例
    • EvictEntity(entityName):从二级缓存中删除命名实例
    • EvictCollection(roleName):从二级缓存中删除集合
    • EvictCollection(roleName, id):从二级缓存中删除指定的集合
    • EvictQueries():从二级缓存中清空全部查询结果集
    • EvictQueries(cacheRegion):从二级缓存中清空指定查询结果集

       下面我们从一个本来会使用缓存的例子中,删除缓存空间。

    复制代码
        class Program
        {
            static void Main(string[] args)
            {
                ISessionFactory sessionFactory = new Configuration().Configure().BuildSessionFactory();
                
                using (ISession session = sessionFactory.OpenSession())
                {
                    Console.WriteLine("第一次查询---------------");
                    IList<PersonModel> ListPerson = session.QueryOver<PersonModel>().Cacheable().CacheMode(CacheMode.Normal).CacheRegion("AllPerson").List();
                    foreach (var p in ListPerson)
                    {
                        Console.WriteLine(p.Name);
                    }
                }
    
                //删除缓存空间
                sessionFactory.EvictQueries("AllPerson");
    
                using (ISession session = sessionFactory.OpenSession())
                {
                    Console.WriteLine("第二次查询---------------");
                    IList<PersonModel> ListPerson = session.QueryOver<PersonModel>().Cacheable().CacheMode(CacheMode.Normal).CacheRegion("AllPerson").List();
                    foreach (var p in ListPerson)
                    {
                        Console.WriteLine(p.Name);
                    }
                }
    
                Console.ReadKey();
            }
        }
    复制代码

      输出结果如下:

      

      我们看到,当我们清空了缓存空间之后,NHibernate不得不重新从数据库读取数据。

  • 相关阅读:
    ActionBar认知
    CSS动画-多列
    CSS3动画效果
    GreenDao数据库框架
    Handler介绍
    AsyncTask介绍
    Android中IntentService的原理及使用
    English interview!
    第六章 结构
    结构体中定义函数指针
  • 原文地址:https://www.cnblogs.com/liuguanghai/p/5031932.html
Copyright © 2020-2023  润新知