NHibernate教程(21)——二级缓存(下)
本节内容
- 引入
- 使用NHibernate二级缓存
- 启用缓存查询
- 管理NHibernate二级缓存
- 结语
引入
这篇我还继续上一篇的话题聊聊NHibernate二级缓存剩下的内容,比如你修改、删除数据时,二级缓存是什么策略呢?我们如果使用缓存查询呢?如何管理NHibernate二级缓存呢?
使用NHibernate二级缓存
不知道具体配置的请转到NHibernate之旅系列文章导航观看上一篇的内容,这篇我们再写几个测试,来看看NHibernate二级缓存一些细节:
测试1:更新数据
当我们启用二级缓存时,如果第一次把数据查询出来,然后修改了这个数据,这时二级缓存中的数据是什么呢?我们写一个测试看看究竟吧:
[Test]
public void SessionFactoryCacheUpdateTest()
{
string firstname="YJingLee";
using (_session)
{
using (var tx = _session.BeginTransaction())
{
Console.WriteLine("第一次读取持久化实例");
Customer customer1 = _session.Get<Customer>(1);
Console.WriteLine("更新持久化实例");
customer1.Name.Firstname =firstname;
tx.Commit();
}
}
ResetSession();
Console.WriteLine("第二次读取持久化实例");
using (_session)
{
Customer customer2 = _session.Get<Customer>(1);
Console.WriteLine("新FirstName为:{0}",customer2.Name.Firstname);
Assert.AreEqual(customer2.Name.Firstname, firstname);
}
}
输出结果:

分析一下:在第一次查询数据时,由于一级、二级缓存中都不存在需要的数据,这时NHibernate从数据库中查询数据。我们修改这条数据并提交到数据库中,NHibernate执行一条更新语句,由于我们设置了读写缓存策略,NHibernate更新了二级缓存中的数据内容,第二次读取这条数据,NHibernate首先从内置缓存(一级缓存)中查找是否存在所需要数据,由于不是在同一个ISession中,所以内置ISession缓存中不存在所需数据,NHibernate则查询二级缓存,这时由于第一次查询了这条数据,所以在二级缓存中存在所需数据,则直接使用缓存中数据。这时缓存中的数据也是更新的。
至于删除、插入数据我想也是类似的。这里我就不写测试了。
启用缓存查询
在NHibernate中,除了缓存持久化类和集合外,查询结果集也可以缓存。如果程序中经常使用同样的条件查询数据,则可以使用查询缓存。在配置文件中可以指定启动查询缓存
<property name="cache.use_query_cache">true</property>
查询缓存后,NHibernate将创建两个缓存区域。一个用于保存查询结果集,由NHibernate.Cache.StandardQueryCache实现。一个用来保存最近更新的查询表的时间截,由NHibernate.Cache.UpdateTimestampsCache实现。
查询缓存中的结果集并不是永久有效的。当缓存的查询语句对应的数据库发生改变时,该缓存结果随之失效。因而对大多数查询而言,查询缓存的益处不是很大,所以NHibernate在默认情况下不对查询进行缓存。
如果需要对查询缓存,还需要显式的使用IQuery.SetCacheable(true)方法。IQuery调用这个方法后,NHibernate将根据查询语句、查询参数、结果集起始范围等信息组成一个IQueryKey。接着根据这个IQueryKey到查询缓存中查找相应数据,查询成功则直接返回查找结果。否则,查询数据库,获取结果集,并把结果集根据IQueryKey放入查询缓存。如果IQueryKey数据发生改变(增加、删除、修改等),这些IQueryKey及其对象的结果集将从缓存中删除。
测试2:显式启用缓存查询
这个例子显式使用IQuery.SetCacheable(true)方法缓存查询结果,第二次查询相同条件时,直接从缓存查询中读取。
[Test]
public void QueryCacheTest()
{
using (_session)
{
Console.WriteLine("第一次查询某数据,显式缓存查询结果");
IList<Customer> customers =
_session.CreateQuery("from Customer c where c.CustomerId > 2")
.SetCacheable(true)
.List<Customer>();
Assert.AreEqual(11, customers.Count);
}
ResetSession();
using (_session)
{
Console.WriteLine("第二次查询某数据,显式缓存查询结果");
IList<Customer> customers =
_session.CreateQuery("from Customer c where c.CustomerId > 2")
.SetCacheable(true)
.List<Customer>();
Assert.AreEqual(11, customers.Count);
}
}
看看结果

由于我们显式缓存查询结果,在第二次查询时,直接使用二级缓存中的结果集。
测试3:指定命名缓存区域
我们还可以使用.SetCacheRegion("cacheRegion")给查询缓存指定了特定的命名缓存区域,该查询缓存的缓存策略将由二级缓存的命名区域负责:
[Test]
public void QueryCacheTest()
{
using (_session)
{
Console.WriteLine("第一次查询某数据,显式缓存查询结果");
IList<Customer> customers =
_session.CreateQuery("from Customer c where c.CustomerId > 2")
.SetCacheable(true)
.SetCacheRegion("queryCache")
.List<Customer>();
Assert.AreEqual(11, customers.Count);
}
ResetSession();
using (_session)
{
Console.WriteLine("第二次查询某数据,显式缓存查询结果");
IList<Customer> customers =
_session.CreateQuery("from Customer c where c.CustomerId > 2")
.SetCacheable(true)
.SetCacheRegion("queryCache")
.List<Customer>();
Assert.AreEqual(11, customers.Count);
}
}
测试结果说明:第一次查询出来的结果集被存储在名为queryCache的缓存区域,第二次同样在这个缓存区域里寻找需要数据,如果第二次没有指定或者指定别的缓存区域则没有需要的数据,就要到数据库中查询了。
测试4:命名查询
可以在映射文件中定义命名查询,<query>元素提供了很多属性,可以用于缓存结果,这里,我举一个例子吧,在Customer.hbm.xml映射文件中定义名为selectCustomer的查询由于查询所有Customer并启用缓存查询,缓存模式为默认方式(下面有说明)
<query cacheable ="true" cache-mode="normal" name="selectCustomer">
from Customer
</query>
编写一个方法:
[Test]
public void NamedQueryCacheTest()
{
using (_session)
{
Console.WriteLine("--->第一次使用命名查询");
IList<Customer> customers = _session.GetNamedQuery("selectCustomer")
.List<Customer>();
}
ResetSession();
using (_session)
{
Console.WriteLine("--->第二次使用命名查询");
IList<Customer> customers = _session.GetNamedQuery("selectCustomer")
.List<Customer>();
}
}
测试结果:第二次直接使用二级缓存中的结果集。

NHibernate提供的查询(HQL、条件查询、原生SQL查询)都类似,我在这里就不重复举例了,大家可以测试下。
管理NHibernate二级缓存
NHibernate二级缓存由ISessionFactory创建并由ISessionFactory自行维护。我们使用NHibernate操作数据时,ISessionFactory能够自动同步缓存,保证缓存的有效性。但是当我们批量操作数据时,往往NHibernate不能维护缓存持久有效。ISessionFactory提供了可编程方式的缓存管理方法。
ISessionFactory提供了一系列的EvictXXX()方法可以方便的从二级缓存中删除一个实例、删除一个集合、一个命名缓存等操作
- Evict(persistentClass):从二级缓存中删除persistentClass类所有实例
- Evict(persistentClass, id):从二级缓存中删除指定的持久化实例
- EvictEntity(entityName):从二级缓存中删除命名实例
- EvictCollection(roleName):从二级缓存中删除集合
- EvictCollection(roleName, id):从二级缓存中删除指定的集合
- EvictQueries():从二级缓存中刷新全部查询结果集
- EvictQueries(cacheRegion):从二级缓存中刷新指定查询结果集
ISession内置缓存可以共享ISessionFactory缓存,通过指定ISession的CacheMode可以控制ISession和ISessionFactory的交互方式。ISession可以通过以下五种方式和ISessionFactory交互:
- Ignore:更新数据时将二级缓存失效,其它时间不和二级缓存交互
- Put:向二级缓存写数据,但不从二级缓存读数据
- Get:从二级缓存读数据,仅在数据更新时向二级缓存写数据
- Normal:默认方式。从二级缓存读/写数据
- Refresh:向二级缓存写数据,想不从二级缓存读数据,通过在配置文件设置cache.use_minimal_puts从数据库中读取数据时,强制二级缓存刷新
测试5:管理NHibernate二级缓存
我们可以使用ISessionFactory提供了一系列的EvictXXX()方法从二级缓存中删除一个实例,看看这个例子在第一次读取持久化实例时,结果集保存在二级缓存中,使用Evict方法从二级缓存中删除所有持久化实例,第二次查询相同数据,二级缓存中不存在则重新从数据库中查询了~~
[Test]
public void SessionFactoryManageTest()
{
ISessionFactory _sessionFactory = (new Configuration()).Configure().BuildSessionFactory();
Console.WriteLine("第一次读取持久化实例");
using (ISession _session = _sessionFactory.OpenSession())
{
Customer customer1 = _session.Get<Customer>(1);
Customer customer2 = _session.Get<Customer>(2);
}
Console.WriteLine("从二级缓存中删除Customer类所有实例"); _sessionFactory.Evict(typeof(Customer));
//也可以_sessionFactory.EvictEntity("DomainModel.Entities.Customer"); Console.WriteLine("第二次读取持久化实例");
using (ISession _session = _sessionFactory.OpenSession())
{
Customer customer1 = _session.Get<Customer>(1);
}
}
输出结果:

测试6:强制刷新缓存区域
我们使用ISession提供的.SetCacheMode(CacheMode.Refresh) 方法可以强制刷新缓存区域,这样可以避免数据不一致问题~~
[Test]
public void QueryCacheTest()
{
using (_session)
{
Console.WriteLine("第一次查询某数据,显式缓存查询结果");
IList<Customer> customers =
_session.CreateQuery("from Customer c where c.CustomerId > 2")
.SetCacheable(true)
.SetCacheRegion("queryCache")
.List<Customer>();
Assert.AreEqual(11, customers.Count);
}
ResetSession();
using (_session)
{
Console.WriteLine("第二次查询某数据,显式缓存查询结果");
Console.WriteLine("----指定特定的命名缓存区域并强制刷新缓存区域----");
IList<Customer> customers =
_session.CreateQuery("from Customer c where c.CustomerId > 2")
.SetCacheable(true)
.SetCacheRegion("queryCache")
.SetCacheMode(CacheMode.Refresh)
.List<Customer>();
Assert.AreEqual(11, customers.Count);
}
}
输出结果:

这篇没有什么深入,不好意思啦~~
结语
好了,这篇就到这里吧!揭晓了比如你修改、删除数据时,二级缓存是什么策略?我们如果使用查询缓存?如何管理NHibernate二级缓存?我们合理使用缓存,可以大幅度地提高程序的性能。
NHibernate教程(21)——二级缓存(下)的更多相关文章
- NHibernate教程(20)——二级缓存(上)
本节内容 引入 介绍NHibernate二级缓存 NHibernate二级缓存提供程序 实现NHibernate二级缓存 结语 引入 上一篇我介绍了NHibernate内置的一级缓存即ISession ...
- NHibernate系列文章十:NHibernate对象二级缓存下
摘要 上一节对NHibernate二级缓存做了简单介绍,NHibernate二级缓存是由SessionFactory管理的,所有Session共享.这一节介绍二级缓存其他两个方面:二级缓存查询和二级缓 ...
- NHibernate教程(19) —— 一级缓存
本节内容 引入 NHibernate一级缓存介绍 NHibernate一级缓存管理 结语 引入 大家看看上一篇了吗?对象状态.这很容易延伸到NHibernate的缓存.在项目中我们灵活的使用NHibe ...
- NHibernate使用MemCache二级缓存
首先,当然是安装MemCache服务器端了. 然后配置过程,仅仅两个问题. 1.NHibernate要与NHibernate.Cache的版本要一致.否则,NHibernate.Caches.MemC ...
- [Nhibernate]二级缓存(二)
目录 写在前面 文档与系列文章 更新数据 二级缓存管理 总结 写在前面 本篇文章也算nhibernate入门系列的结尾了,在总结nhibernate系列的过程中,遇到了很多问题,学习的过程也是解决bu ...
- [Nhibernate]二级缓存
[Nhibernate]二级缓存 目录 写在前面 文档与系列文章 二级缓存 Nhibernate二级缓存提供程序 一个例子 总结 写在前面 上篇文章介绍了nhibernate中一级缓存的相关内容,一级 ...
- 基于NHibernate二级缓存的MongoDB组件
设计一套基于NHibernate二级缓存的MongoDB组件(上) 摘要:NHibernate Contrib 支持很多第三方的二级缓存,如SysCache,MemCache,Prevalence ...
- [Nhibernate]二级缓存(一)
目录 写在前面 文档与系列文章 二级缓存 Nhibernate二级缓存提供程序 一个例子 总结 写在前面 上篇文章介绍了nhibernate中一级缓存的相关内容,一级缓存过期时间和ISession对象 ...
- NHibernate系列文章九:NHibernate对象二级缓存上
摘要 NHibernate的二级缓存由SessionFactory管理,由所有Session共享. NHibernate缓存读取顺序: 首先从一级缓存中读取,如果一级缓存对象存在,则读取一级缓存对象并 ...
随机推荐
- AVR开发 Arduino方法(一) 端口子系统
Arduino UNO R3使用的主处理器ATMega328P上有3个8位的输入/输出端口,它们分别是PB,PC和PD.Arduino IDE提供的Blink示例可以帮助我们了解端口的数字输出功能: ...
- vue+mockjs 模拟数据,实现前后端分离开发
在项目中尝试了mockjs,mock数据,实现前后端分离开发. 关于mockjs,官网描述的是 1.前后端分离 2.不需要修改既有代码,就可以拦截 Ajax 请求,返回模拟的响应数据. 3.数据类型丰 ...
- NYOJ 69 数的长度(数学)
数的长度 时间限制:3000 ms | 内存限制:65535 KB 难度:1 描述 N!阶乘是一个非常大的数,大家都知道计算公式是N!=N*(N-1)······*2*1.现在你的任务是计算出 ...
- 不受控制的 position:fixed
本文为纯理论文章,没有 Demo,没有配图,可能会略微枯燥. 大家都知道,position:fixed 在日常的页面布局中非常常用,在许多布局中起到了关键的作用.它的作用是: position:fix ...
- WPF中实现多选ComboBox控件
在WPF中实现带CheckBox的ComboBox控件,让ComboBox控件可以支持多选. 将ComboBox的ItemsSource属性Binding到一个Book的集合, public clas ...
- 友盟崩溃日志分析工具 - dSYMTools
公司的项目中集成了UM的统计功能,UM统计可以统计app的用户新增,版本分布,日志崩溃等信息,打开错误分析按钮,则可能会看到很多崩溃的日志信息 选择编辑可以选择更多的版本号 点击列表中的一个崩溃日志, ...
- 关于JS中利用for循环解决实际问题的逻辑操作
第一部分 <script> // <!--折纸:折多少次和珠穆朗玛峰一样高//1.一张纸的厚度是0.0001米,将纸对折,对折多少次厚度超过珠峰高度884 ...
- 常见C++面试题及基本知识点总结(一)
[转载请注明出处]:http://www.cnblogs.com/LUO77/p/5771237.html 1. 结构体和共同体的区别. 定义: 结构体struct:把不同类型的数据组合成一个整体, ...
- 使用Openfire配置安卓客户端聊天服务器
Openfire是一种即时通信服务器,采用开放的XMPP协议,您可以使用各种支持XMPP协议的IM客户端软件登陆服务. Openfire 采用Java开发,开源的实时协作(RTC)服务器基于XMPP( ...
- .net core 2.0学习笔记(三):度量.net framework 迁移到.net core的工作量
把现有的.net framework程序迁移到.net core上,是一个非常复杂的工作,特别是一些API在两个平台上还不能同时支持.两个类库的差异性,通过人工很难识别全.好在微软的工程师们考虑到了我 ...