本節內容
引入
介紹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" ?>
Step6:測試代碼
<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>
在不同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二級緩存,並用一個查詢例子說明了一切,但是關於二級緩存還有很多內容,比如你修改、刪除數據時,二級緩存是什麼策略呢?我們如果使用查詢緩存呢?如何管理NHibernate二級緩存呢?這就在下一篇揭曉吧。