天天看點

NHibernate之旅(23):探索NHibernate二級緩存(上)

本節内容

  • 引入
  • 介紹NHibernate二級緩存
  • NHibernate二級緩存提供程式
  • 實作NHibernate二級緩存
  • 結語

引入

上一篇我介紹了NHibernate内置的一級緩存即ISession緩存。這篇我們來了解下NHibernate二級緩存即ISessionFactory級别緩存。二級緩存是可擴充的,在NHibernate Contrib上提供了第三方NHibernate二級緩存提供程式。

介紹NHibernate二級緩存

NHibernate二級緩存由ISessionFactory建立,可以被所有的ISession共享。

在NHibernate中,當我們啟用NHibernate二級緩存。使用ISession進行資料操作時,NHibernate首先從内置緩存(一級緩存)中查找是否存在需要的資料,如果内置緩存不存在需要的資料,則查詢二級緩存,如果二級緩存中存在所需資料,則直接使用緩存中資料,否則從資料庫中查詢資料并放入緩存中。

NHibernate本身提供了一個基于Hashtable的HashtableCache緩存,但是功能非常有限而且性能比較差,不适合在大型應用程式使用,我們可以使用第三方緩存提供程式作為NHibernate二級緩存實作。

但是,使用緩存的缺點就是如果緩存政策設定不當,NHibernate不知道其它應用程式對資料庫的修改及時更新緩存。是以,建議隻對系統經常使用、資料量不大且不會被其它應用程式修改的隻讀資料(或很少被修改的資料)使用緩存。

NHibernate二級緩存提供程式

NHibernate提供了NHibernate.Cache.ICacheProvider接口用來支援第三方緩存提供程式實作。開發緩存提供程式時,需要實作該接口作為NHibernate和緩存實作直接的擴充卡。NHibernate提供了常見的緩存提供程式的内置擴充卡,這些擴充卡都實作了NHibernate.Cache.ICacheProvider接口。

除了NHibernate本身提供的一個基于Hashtable的HashtableCache緩存,在NHibernate Contrib上提供了六種第三方NHibernate二級緩存提供程式,完全開源的。我們直接下載下傳其程式集引用到我們的項目中就可以使用了。

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

實作NHibernate二級緩存

NHibernate二級緩存是一個可插拔的元件。在預設情況下,NHibernate不啟動二級緩存。如果要使用二級緩存則需要在NHibernate配置檔案中顯式的啟用二級緩存。NHibernate二級緩存可以分别為每一個具體的類和集合配置應用級或分布式緩存。

緩存并發政策

提示一下,在NHibernate官方文檔中有介紹,詳情請參考NHibernate官方文檔。當兩個獨立的事務同時通路資料庫時,可能産生丢失更新、不可重複讀等并發問題。同樣,當兩個并發事務同時通路緩存時,也有可能産生各種并發問題。是以,在緩存級别也需要設定相應的并發通路政策。

NHibernate内置四種并發通路政策:

  • read-only:隻讀緩存。适用于隻讀資料。可用于群集中。
  • read-write:讀寫緩存。
  • nonstrict-read-write:非嚴格讀寫緩存。不保證緩存與資料庫的一緻性。
  • transactional:事務緩存。提供可重複讀的事務隔離級别。

我們動手實作二級緩存吧~~~

Step1:配置第三方緩存提供程式

我們在NHibernate配置檔案中通過cache.provider_class屬性顯式指定緩存實作,屬性值為緩存擴充卡的具體類名。如果你使用上面的第三方緩存提供程式,還需要配置緩存提供程式本身。這裡我設定NHibernate本身提供了一個基于Hashtable的HashtableCache緩存。

<property name="cache.provider_class">NHibernate.Cache.HashtableCacheProvider</property>      

Step2:顯式啟用二級緩存

在NHibernate配置檔案中使用cache.use_second_level_cache屬性顯式啟用二級緩存,參數為Bool值,這裡啟用設定為true。

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

Step3:配置第三方緩存提供程式本身

如果你使用第三方緩存提供程式,那麼需要對第三方緩存提供程式本身進行配置,需要詳細配置第三方緩存提供程式緩存屬性:儲存時間、過期時間、可以緩存對象數量。這裡我就使用NHibernate本身提供的HashtableCache緩存,是以這一步就省略了。

Step4:為每一個持久化類和集合指定相應的緩存政策

方法一:在映射檔案中通過<cache>元素配置類和集合的緩存政策,在Class元素或者集合元素中添加<cache>元素進行配置。注意:<cache>元素必須在<id>元素之前。

<cache usage="read-only|read-write|nonstrict-read-write" region="預設類或集合名稱"/>      

方法二:在NHibernate配置檔案hibernate.cfg.xml中通過<class-cache>元素和<collection-cache>元素分别配置類和集合的緩存政策。

我還是建議大家使用NHibernate配置檔案定義緩存政策,這樣可以避免在各個映射檔案配置緩存定義而增大維護難度。

指定類:

<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:聲明緩存同步政策,就是上面說明的四種緩存政策。

配置檔案和映射檔案定義不一樣,不知道是不是BUG。

Step5:開始測試

在測試之前,我們先看看上面的步驟我們完成了哪些内容。我貼出具體代碼:

代碼片段1:NHibernate配置檔案:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-configuration  xmlns="urn:nhibernate-configuration-2.2" >
    <session-factory name="NHibernateSample.DAL.Test">
    <!--
       配置二級緩存執行個體檔案
       作者:李永京(YJingLee's Blog)
       出處:http://lyj.cnblogs.com
    -->
    <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
    <property name="connection.connection_string">
      Data Source=.\SQLEXPRESS;Initial Catalog=NHibernateSample;
      Integrated Security=True;Pooling=False</property>
    <property name="adonet.batch_size">10</property>
    <property name="show_sql">true</property>
    <property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
    <property name="use_outer_join">true</property>
    <property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>
    <!--1.配置二級緩存提供程式-->
    <property name="cache.provider_class">NHibernate.Cache.HashtableCacheProvider</property>
    <!--2.顯式啟用二級緩存-->
    <property name ="cache.use_second_level_cache">true</property>
    <!--4.啟動查詢緩存(注:下一篇内容:http://lyj.cnblogs.com)-->
    <property name="cache.use_query_cache">true</property>
    <mapping assembly="DomainModel"/>
    <!--3.配置映射的二級緩存-->
    <class-cache class="DomainModel.Entities.Customer,DomainModel" usage="read-write"/>
    </session-factory>
</hibernate-configuration>      

代碼片段2:Customer.hbm.xml映射檔案:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" 
                   assembly="DomainModel" namespace="DomainModel">
    <!--
       配置二級緩存映射檔案
       作者:李永京(YJingLee's Blog)
       出處:http://lyj.cnblogs.com
    -->
  <class name ="DomainModel.Entities.Customer,DomainModel" 
         table="Customer">
    <cache usage="read-write"/>
    <id name="CustomerId" type="Int32" unsaved-value="0">
      <generator class ="native"></generator>
    </id>
    <version name="Version" type="integer" unsaved-value="0"/>    
    <component name="Name" class="DomainModel.Entities.Name,DomainModel">
      <property name="Firstname"/>
      <property name ="Lastname"/>
    </component> 
    <set name="Orders" table="`Order`" generic="true" inverse="true">
      <cache usage="read-only"/>
      <key column="Customer" foreign-key="FK_CustomerOrders"/>
      <one-to-many class="DomainModel.Entities.Order,DomainModel"/>
    </set>
  </class>
</hibernate-mapping>      

Step6:測試代碼

在不同Session中擷取實體:

[Test]
public void SessionFactoryCacheTest()
{
     using (_session)
    {
        Console.WriteLine("--Session 1--讀取持久化執行個體--");
        Customer customer1 = _session.Get<Customer>(1);
        Assert.IsNotNull(customer1);
    }
    ResetSession();
    using (_session)
    {
        Console.WriteLine("--Session 2--讀取持久化執行個體--");
        Customer customer2 = _session.Get<Customer>(1);
        Assert.IsNotNull(customer2);
    }
}      

分析一下:在第一次查詢資料時,由于一級、二級緩存中都不存在需要的資料,這時NHibernate從資料庫中查詢資料。第二次讀取同一資料,NHibernate首先從内置緩存(一級緩存)中查找是否存在所需要資料,由于不是在同一個ISession中,是以内置ISession緩存中不存在所需資料,NHibernate則查詢二級緩存,這時由于第一次查詢了這條資料,是以在二級緩存中存在所需資料,則直接使用緩存中資料。看看輸出結果吧:

NHibernate之旅(23):探索NHibernate二級緩存(上)

結語

好了,這篇就到這裡吧!我們初步認識了NHibernate二級緩存,并用一個查詢例子說明了一切,但是關于二級緩存還有很多内容,比如你修改、删除資料時,二級緩存是什麼政策呢?我們如果使用查詢緩存呢?如何管理NHibernate二級緩存呢?這就在下一篇揭曉吧。

本系列連結:NHibernate之旅系列文章導航

NHibernate Q&A

  • 歡迎加入NHibernate中文社群,一起讨論NHibernate知識!
  • 請到NHibernate中文社群下載下傳本系列相關源碼。

下次繼續分享NHibernate!