[Nhibernate]二級緩存(一)


目錄

寫在前面

文檔與系列文章

二級緩存

Nhibernate二級緩存提供程序

一個例子

總結

寫在前面

上篇文章介紹了nhibernate中一級緩存的相關內容,一級緩存過期時間和ISession對象的生命周期相同,並且不同的Session不能共享緩存,一級緩存也可以成為ISession緩存。那么現在我們就學一下nhibernate中的二級緩存,即ISessionFactory級別緩存,可被所有的ISession所共享。二級緩存是可擴展的,在http://sourceforge.net/projects/nhcontrib/上提供了第三方的Nhibernate二級緩存提供程序。

文檔與系列文章

[Nhibernate]體系結構

[NHibernate]ISessionFactory配置

[NHibernate]持久化類(Persistent Classes)

[NHibernate]O/R Mapping基礎

[NHibernate]集合類(Collections)映射 

[NHibernate]關聯映射

[NHibernate]Parent/Child

[NHibernate]緩存(NHibernate.Caches)

[NHibernate]NHibernate.Tool.hbm2net

[NHibernate]Nullables

[NHibernate]Nhibernate如何映射sqlserver中image字段

[NHibernate]基本配置與測試 

[NHibernate]HQL查詢 

[NHibernate]條件查詢Criteria Query

[NHibernate]增刪改操作

[NHibernate]事務

[NHibernate]並發控制

[NHibernate]組件之依賴對象

[NHibernate]一對多關系(級聯刪除,級聯添加)

[NHibernate]一對多關系(關聯查詢)

[NHibernate]多對多關系(關聯查詢)

[NHibernate]延遲加載

[NHibernate]立即加載

[NHibernate]視圖處理

[NHibernate]N+1 Select查詢問題分析

[NHibernate]存儲過程的使用(一)

[NHibernate]存儲過程的使用(二)

[NHibernate]存儲過程的使用(三)

[Nhibernate]SchemaExport工具的使用(一)——通過映射文件修改數據表

[Nhibernate]SchemaExport工具的使用(二)——創建表及其約束、存儲過程、視圖

[Nhibernate]對象狀態

[Nhibernate]一級緩存

二級緩存

關於二級緩存的詳細可以參考[NHibernate]緩存(NHibernate.Caches)

NHibernate session有一個內部的(一級)緩存,存放着它的實體。這些緩存沒有共享,因此session被銷毀時它的緩存也被銷毀了。NHibernate提供了二級緩存系統;它在SessionFactory級別工作。因此它被同一個SessionFactory產生的session共享。

在NHibernate中,當我們啟用NHibernate二級緩存。

使用ISession進行數據操作時,NHibernate首先從內置緩存(一級緩存)中查找是否存在需要的數據,如果內置緩存不存在需要的數據,則查詢二級緩存,如果二級緩存中存在所需數據,則直接使用緩存中數據,否則從數據庫中查詢數據並放入緩存中。

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

使用緩存的缺點:

如果緩存策略設置不當,NHibernate不知道其它應用程序對數據庫的修改及時更新緩存。因此,建議只對系統經常使用、數據量不大且不會被其它應用程序修改的只讀數據(或很少被修改的數據)使用緩存。

Nhibernate二級緩存提供程序

NHibernate提供了NHibernate.Cache.ICacheProvider接口用來支持第三方緩存提供程序實現。開發緩存提供程序時,需要實現該接口作為NHibernate和緩存實現直接的適配器。NHibernate提供了常見的緩存提供程序的內置適配器,這些適配器都實現了NHibernate.Cache.ICacheProvider接口。

NHibernate.Cache.ICacheProvider定義如下:

 1 namespace NHibernate.Cache
 2 {
 3     // 摘要: 
 4     //     Support for pluggable caches
 5     public interface ICacheProvider
 6     {
 7         // 摘要: 
 8         //     Configure the cache
 9         //
10         // 參數: 
11         //   regionName:
12         //     the name of the cache region
13         //
14         //   properties:
15         //     configuration settings
16         ICache BuildCache(string regionName, IDictionary<string, string> properties);
17         //
18         // 摘要: 
19         //     generate a timestamp
20         long NextTimestamp();
21         //
22         // 摘要: 
23         //     Callback to perform any necessary initialization of the underlying cache
24         //     implementation during ISessionFactory construction.
25         //
26         // 參數: 
27         //   properties:
28         //     current configuration settings
29         void Start(IDictionary<string, string> properties);
30         //
31         // 摘要: 
32         //     Callback to perform any necessary cleanup of the underlying cache implementation
33         //     during NHibernate.ISessionFactory.Close().
34         void Stop();
35     }
36 }
NHibernate.Cache.ICacheProvider

除了NHibernate本身提供的一個基於Hashtable的HashtableCache緩存。

NHibernate Contrib上提供了六種第三方NHibernate二級緩存提供程序,完全開源的。我們直接下載其程序集引用到我們的項目中就可以使用了。

  1. NHibernate.Caches.MemCache
  2. NHibernate.Caches.Prevalence
  3. NHibernate.Caches.SharedCache
  4. NHibernate.Caches.SysCache
  5. NHibernate.Caches.SysCache2
  6. NHibernate.Caches.Velocity

一個例子

如何使用?

在默認情況下,NHibernate不啟動二級緩存。如果要使用二級緩存則需要在NHibernate配置文件中顯式的啟用二級緩存。NHibernate二級緩存可以分別為每一個具體的類和集合配置應用級或分布式緩存。

緩存並發策略

當兩個獨立的事務同時訪問數據庫時,可能產生丟失更新、不可重復讀等並發問題。同樣,當兩個並發事務同時訪問緩存時,也有可能產生各種並發問題。因此,在緩存級別也需要設置相應的並發訪問策略。

NHibernate內置四種並發訪問策略:

  • read-only:只讀緩存。適用於只讀數據。可用於群集中。
  • read-write:讀寫緩存。
  • nonstrict-read-write:非嚴格讀寫緩存。不保證緩存與數據庫的一致性。
  • transactional:事務緩存。提供可重復讀的事務隔離級別。

配置緩存

在NHibernate配置文件中通過cache.provider_class屬性顯式指定緩存實現,屬性值為緩存適配器的具體類名。如果你使用上面的第三方緩存提供程序,還需要配置緩存提供程序本身,關於第三方緩存提供程序的配置可以參考文檔[NHibernate]緩存(NHibernate.Caches)。首先是從Nhibernate本身自帶的HashtableCache緩存入手,學習一下緩存如何配置。

在nhibernate配置文件中顯示指定緩存提供者類

1 <property name="cache.provider_class">NHibernate.Cache.HashtableCacheProvider</property>
1     <!--顯示啟用二級緩存,用cache.use_second_level_cache屬性顯式啟用二級緩存,參數為Bool值,這里啟用設置為true-->
2     <property name="cache.use_second_level_cache">true</property>

如果使用第三方緩存提供程序,那么需要對第三方緩存提供程序本身進行配置,需要詳細配置第三方緩存提供程序緩存屬性:保存時間、過期時間、可以緩存對象數量。

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

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

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

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

1 <class-cache class="類名稱" region="默認類名稱" include="all|non-lazy"
2              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:聲明緩存同步策略,就是上面說明的四種緩存策略。

有兩種方式定義緩存策略,到底選擇那種好?

在nhibernate配置文件中你可以為每個類設置更方便,維護起來也方便,如果在每個持久化類中分別設置緩存策略,維護起來有點麻煩。

測試

在nhibernate中啟用二級緩存,並指定緩存Customer類

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <hibernate-configuration  xmlns="urn:nhibernate-configuration-2.2" >
 3   <session-factory>
 4     <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
 5     <property name="connection.connection_string">
 6       server=.;database=shop;uid=sa;pwd=sa
 7     </property>
 8     <property name="dialect">NHibernate.Dialect.MsSql2008Dialect</property>
 9     <property name="show_sql">true</property>
10     <!--二級緩存配置-->
11     <!--cache.provider_class屬性顯式指定緩存實現,屬性值為緩存適配器的具體類名-->
12     <property name="cache.provider_class">NHibernate.Cache.HashtableCacheProvider</property>
13     <!--顯示啟用二級緩存,用cache.use_second_level_cache屬性顯式啟用二級緩存,參數為Bool值,這里啟用設置為true-->
14     <property name="cache.use_second_level_cache">true</property>
15     <!--啟用查詢緩存-->
16     <property name ="cache.use_query_cache">true</property>
17     <mapping  assembly="Wolfy.Shop.Domain"/>
18     <!--Customer類啟用二級緩存-->
19     <class-cache class="Wolfy.Shop.Domain.Entities.Customer,Wolfy.Shop.Domain" usage="read-write"/>
20   </session-factory>
21 </hibernate-configuration>

Customer.hbm.xml映射文件

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <!--assembly:程序集,namespace:命名空間-->
 3 <hibernate-mapping  xmlns="urn:nhibernate-mapping-2.2" assembly="Wolfy.Shop.Domain"  namespace="Wolfy.Shop.Domain.Entities">
 4   <!--存儲過程-->
 5   <class name="Wolfy.Shop.Domain.Entities.Customer,Wolfy.Shop.Domain" table="TB_Customer">
 6     <!--二級緩存-->
 7     <cache usage="read-write"/>
 8     <!--主鍵-->
 9     <id name="CustomerID" type="Guid" unsaved-value="null">
10       <column name="CustomerID" sql-type="uniqueidentifier" not-null="true" unique="true" />
11       <generator class="assigned"></generator>
12     </id>
13     <!--版本控制-->
14     <version name="Version" column="Version" type="integer"  unsaved-value="0"/>
15     <!--組件 name組件屬性名-->
16     <component name="NameAddress" class="Wolfy.Shop.Domain.Entities.Name,Wolfy.Shop.Domain">
17       <!--Name類中的屬性property-->
18       <property name="CustomerName" column ="CustomerName" type="string"
19                            length="16" not-null="false" />
20       <property name ="CustomerAddress" column="CustomerAddress" type="string"
21                            length="128" not-null="false" />
22     </component>
23     <!--一對多關系:一個客戶可以有一個或者多個訂單-->
24     <!--子實體負責維護關聯關系-->
25     <set name="Orders" table="TB_Order" generic="true" inverse="true" cascade="all">
26       <key column="CustomerID" foreign-key="FK_TB_Order_TB_Customer"></key>
27       <one-to-many class="Wolfy.Shop.Domain.Entities.Order,Wolfy.Shop.Domain"/>
28     </set>
29     <!--存儲過程,check參數:none/rowcount/param-->
30     <sql-insert>exec TB_CustomerInsert ?,?,?,?</sql-insert>
31     <!--<sql-update>exec TB_CustomerUpdate ?,?,?,?</sql-update>-->
32     <sql-update>UPDATE TB_CustomerUpdate SET Version=?, [CustomerName]=?,[CustomerAddress]=? WHERE CustomerID=? AND Version=? </sql-update>
33     <!--<sql-delete check="rowcount" >exec TB_CustomerDelete ?</sql-delete>-->
34     <sql-delete>DELETE FROM [TB_Customer] WHERE [CustomerID] = ? and [Version] =?</sql-delete>
35   </class>
36   <!--需要和class節點同一級別-->
37   <sql-query name="ps_Search" >
38     <!--<return class="Wolfy.Shop.Domain.Entities.Customer,Wolfy.Shop.Domain" />-->
39     <return-scalar column="CustomerName" type="String"/>
40     exec ps_Search :CustomerID
41   </sql-query>
42 </hibernate-mapping>
View Code

在不同的Session中查詢實體

 1        /// <summary>
 2         /// 根據客戶id查詢
 3         /// </summary>
 4         /// <param name="customerID"></param>
 5         /// <returns></returns>
 6         public Customer GetCustomerById(Guid customerID)
 7         {
 8             ISession session = NHibernateHelper.GetSession();
 9             return session.Get<Customer>(customerID);
10         }
11         /// <summary>
12         /// 根據客戶id查詢
13         /// </summary>
14         /// <param name="customerID"></param>
15         /// <returns></returns>
16         public Customer GetCustomerById2(Guid customerID)
17         {
18             //重置Session
19             ISession session = NHibernateHelper.ResetSession();
20             return session.Get<Customer>(customerID);
21         }

單元測試

 1       [TestMethod]
 2         public void GetCustomerById2Test()
 3         {
 4             Console.WriteLine("Session1 第一次加載");
 5             Customer c1 = _customerData.GetCustomerById(new Guid("DDF63750-3307-461B-B96A-7FF356540CB8"));
 6             Assert.IsNotNull(c1);
 7             Console.WriteLine("Session2 第二次加載");
 8             Customer c2 = _customerData.GetCustomerById2(new Guid("DDF63750-3307-461B-B96A-7FF356540CB8"));
 9             Assert.IsNotNull(c2);
10         }

在第一次查詢數據時,由於一級、二級緩存中都不存在需要的數據,這時NHibernate從數據庫中查詢數據。第二次讀取同一數據,NHibernate首先從內置緩存(一級緩存)中查找是否存在所需要數據,由於不是在同一個ISession中,所以內置ISession緩存中不存在所需數據,NHibernate則查詢二級緩存,這時由於第一次查詢了這條數據,所以在二級緩存中存在所需數據,則直接使用緩存中數據。(該測試方法與一級緩存中的測試方法相同,為了方便對比,將上篇文章中的圖貼在一起進行對比)

一級緩存測試結果

二級緩存測試結果

 

通過對比,可以發現在單元測試中第二次會話,斷言c2不為null通過了測試,說明c2是從緩存中取的。也印證了ISessionFactory級別的二級緩存是可以共享緩存的。

總結

在學習nhibernate過程中難免遇到各種各樣的錯誤,學習的過程也是解決各種異常的過程。

關於二級緩存中簡單的查詢緩存就介紹到這里,下篇文章將介紹二級緩存針對刪除,修改的策略及緩存的管理的內容。

參考文章

http://www.cnblogs.com/lyj/archive/2008/11/27/1341885.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM