一、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不得不重新从数据库读取数据。

NHibernate二级缓存(第十一篇)的更多相关文章

  1. [Nhibernate]二级缓存(二)

    目录 写在前面 文档与系列文章 更新数据 二级缓存管理 总结 写在前面 本篇文章也算nhibernate入门系列的结尾了,在总结nhibernate系列的过程中,遇到了很多问题,学习的过程也是解决bu ...

  2. 基于NHibernate二级缓存的MongoDB组件

    设计一套基于NHibernate二级缓存的MongoDB组件(上)   摘要:NHibernate Contrib 支持很多第三方的二级缓存,如SysCache,MemCache,Prevalence ...

  3. [Nhibernate]二级缓存(一)

    目录 写在前面 文档与系列文章 二级缓存 Nhibernate二级缓存提供程序 一个例子 总结 写在前面 上篇文章介绍了nhibernate中一级缓存的相关内容,一级缓存过期时间和ISession对象 ...

  4. 01-08-05【Nhibernate (版本3.3.1.4000) 出入江湖】NHibernate二级缓存:第三方MemCache缓存

    一.准备工作 [1]根据操作系统(位数)选择下载相应版本的MemCache, MemCache的下载和安装,参看: http://www.cnblogs.com/easy5weikai/p/37606 ...

  5. [Nhibernate]二级缓存

    [Nhibernate]二级缓存 目录 写在前面 文档与系列文章 二级缓存 Nhibernate二级缓存提供程序 一个例子 总结 写在前面 上篇文章介绍了nhibernate中一级缓存的相关内容,一级 ...

  6. NHibernate系列文章九:NHibernate对象二级缓存上

    摘要 NHibernate的二级缓存由SessionFactory管理,由所有Session共享. NHibernate缓存读取顺序: 首先从一级缓存中读取,如果一级缓存对象存在,则读取一级缓存对象并 ...

  7. NHibernate教程(21)——二级缓存(下)

    本节内容 引入 使用NHibernate二级缓存 启用缓存查询 管理NHibernate二级缓存 结语 引入 这篇我还继续上一篇的话题聊聊NHibernate二级缓存剩下的内容,比如你修改.删除数据时 ...

  8. NHibernate教程(20)——二级缓存(上)

    本节内容 引入 介绍NHibernate二级缓存 NHibernate二级缓存提供程序 实现NHibernate二级缓存 结语 引入 上一篇我介绍了NHibernate内置的一级缓存即ISession ...

  9. NHibernate系列文章十:NHibernate对象二级缓存下

    摘要 上一节对NHibernate二级缓存做了简单介绍,NHibernate二级缓存是由SessionFactory管理的,所有Session共享.这一节介绍二级缓存其他两个方面:二级缓存查询和二级缓 ...

随机推荐

  1. C++之++运算符重载问题

    记录++之前先记一下左右值和存取数据的问题 数据的存放分三个部分,堆区,栈区和静态变量区 左值可以更改,右值不能更改 栈区和堆区存储的都是左值,可以随意更改其值,静态变量区部分数据是右值,比如cons ...

  2. [CF3B]Lorry

    题目大意: 有$n(n\leq 10^5)$个物品,背包的容量为$m(m\leq 10^9)$.每个物品有重量$w_i(w_i\in\{1,2\})$和价值$v_i(v_i\leq 10^4)$.问最 ...

  3. 发掘StateListAnimator的全部潜能

    原文地址:https://blog.stylingandroid.com/statelistanimator/ 原文作者:Leave a reply 译文出自:安卓巴士 译者: MrlLee 校对者: ...

  4. IMAP IDLE模式(推送邮件)

    在电子邮件技术中,IDLE是RFC 2177中描述的一项IMAP功能,它允许客户端向服务器表明它已准备好接受实时通知. Internet消息访问协议IMAP4协议,它要求客户端轮询服务器来更改所选中的 ...

  5. JS编码三种方法的区别:escape、encodeURI和encodeURIComponent

    1.escape和它们不是同一类 简单来说,escape是对字符串(string)进行编码(而另外两种是对URL),作用是让它们在所有电脑上可读.编码之后的效果是%XX或者%uXXXX这种形式.其中  ...

  6. consist of, made up of

    consist vi.由……组成:由……构成(常和介词of构成固定搭配)made up of由……组成[例如] One year consists of 365 days.一年有365天.The te ...

  7. fabricjs 自定义类型

    https://stackoverflow.com/questions/36660108/how-to-create-custom-fabricjs-object I have to create a ...

  8. 如何让mysql的自动递增的字段重新从1开始呢?(

    数据库表自动递增字段在用过一段时间后清空,还是继续从清空后的自动编号开始.如何才能让这个字段自动从1开始自动递增呢? 下面两个方法偶都试过,很好用: 1 清空所有数据,将自增去掉,存盘,在加上自增,存 ...

  9. Java三大器之过滤器(Filter)的工作原理和代码演示

    一.Filter简介 Filter也称之为过滤器,它是Servlet技术中最激动人心的技术之一,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp,Servlet, 静 ...

  10. JAVA_Exception starting filter struts2怎么办

    1 请确保你的项目里面有这两个文件,没有则导入   2 如果还不行,并且你设置了Struts的开发模式,并且你的Tomcat的路径有空格,大部分情况是Program File的原因,此时你需要重新安装 ...