一、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. XML签名

    英文:https://www.javacodegeeks.com/2013/10/xml-security-with-digital-signature-in-java.html 中文:http:// ...

  2. PMBOK及PMP考试精要

    PROJECT MANAGEMENT KNOWLEDGE AREAS项目管理知识体系 2 PROJECT LIFE CYCLE / PROJECT MANAGEMENT PROCESS GROUPS项 ...

  3. [Contest20180418]物理竞赛

    题意:在一个三维空间中有一个轴,轴上有一个垂直于轴的半径为$R$的凸透镜(光心在轴上)和$n$个点光源,假设每个点光源发出的$1$单位光都刚好覆盖凸透镜,现在有一个半径为$r$的圆形光屏,问光屏最多能 ...

  4. CSS3动画那么强,requestAnimationFrame还有毛线用?

    一.哟,requestAnimationFrame, 新同学,先自我介绍下 Hello, 大家好,我就是风姿卓越,万种迷人的requestAnimationFrame,呵呵呵呵.很高兴和大家见面,请多 ...

  5. /etc/fstab 官方文档

    1什么是fstab 2fstab文件示例 3fstab 文件组成 4文件系统标识 4.1Kernel naming 4.2UUID 4.3Label 5建议 5.1atime 参数 5.2tmpfs ...

  6. docker之人手一台服务器

    安装docker uname –r 检查内核版本 yum update 升级本地yum包 vim /etc/yum.repos.d/docker.repo #添加yum仓库配置 [dockerrepo ...

  7. PHP登陆提示信息框代码

    出错的提示框 echo "<Script Language='Javascript'>";   echo "alert('提示:用户名密码错误,请重新输入!' ...

  8. 机器学习算法实践:朴素贝叶斯 (Naive Bayes)(转载)

    前言 上一篇<机器学习算法实践:决策树 (Decision Tree)>总结了决策树的实现,本文中我将一步步实现一个朴素贝叶斯分类器,并采用SMS垃圾短信语料库中的数据进行模型训练,对垃圾 ...

  9. 【Zookeeper】Zookeeper部署笔记

    Zookeeper部署笔记 .上传zk安装包 .解压 .配置(先在一台节点上配置) .1添加一个zoo.cfg配置文件 $ZOOKEEPER/conf mv zoo_sample.cfg zoo.cf ...

  10. 【转】Understanding Inversion of Control, Dependency Injection and Service Locator Print

    原文:https://www.dotnettricks.com/learn/dependencyinjection/understanding-inversion-of-control-depende ...