[Nhibernate]二级缓存(二)
目录
写在前面
本篇文章也算nhibernate入门系列的结尾了,在总结nhibernate系列的过程中,遇到了很多问题,学习的过程也是解决bug的过程,在学习nhibernate的过程中还学习了单元测试的使用,这个是附属产品,我也没有想到,算是意外收获吧。这个系列学完了,正好公司有个项目,马上就要立项,正好能将学到的东西运用到实际的项目中,想想就让人激动。当然,我相信在实际的项目中,肯定还会遇到问题,但是通过本系列的学习,我已经不怕遇到bug了,解决bug的过程也是成长。废话不多说了,进入本篇学习的内容吧。这篇继续上一篇NHibernate二级缓存剩下的内容,比如你修改、删除数据时,二级缓存是什么策略呢?我们如果使用缓存查询呢?如何管理NHibernate二级缓存呢?
文档与系列文章
[NHibernate]持久化类(Persistent Classes)
[NHibernate]集合类(Collections)映射
[NHibernate]缓存(NHibernate.Caches)
[NHibernate]NHibernate.Tool.hbm2net
[NHibernate]Nhibernate如何映射sqlserver中image字段
[NHibernate]条件查询Criteria Query
[Nhibernate]SchemaExport工具的使用(一)——通过映射文件修改数据表
[Nhibernate]SchemaExport工具的使用(二)——创建表及其约束、存储过程、视图
更新数据
在启用二级缓存时候,我们将数据查询出来后,如果对数据进行了修改,再查看二级缓存中的数据,此时的数据是什么呢?
一个例子
修改CustomerData类,添加如下三个方法用于测试
/// <summary>
/// 开启二级缓存的情况下,修改customer对象
/// </summary>
/// <param name="customer"></param>
public void ISessionFactoryCacheUpdate(Customer customer)
{
ISession session = NHibernateHelper.GetSession();
using (var trans=session.BeginTransaction())
{
try
{
session.SaveOrUpdate(customer);
session.Flush();
trans.Commit();
}
catch (Exception)
{
trans.Rollback();
throw;
}
}
}
/// <summary>
/// 根据客户id查询
/// </summary>
/// <param name="customerID"></param>
/// <returns></returns>
public Customer GetCustomerById(Guid customerID)
{
ISession session = NHibernateHelper.GetSession();
return session.Get<Customer>(customerID);
}
/// <summary>
/// 开启二级缓存情况下,重置ISession然后,根据客户id查询
/// </summary>
/// <param name="customerID"></param>
/// <returns></returns>
public Customer GetCustomerById2(Guid customerID)
{
//重置Session
ISession session = NHibernateHelper.ResetSession();
return session.Get<Customer>(customerID);
}
单元测试
[TestMethod]
public void ISessionFactoryCacheUpdateTest()
{
string strCustomerName = "zhangsan2222";
Console.WriteLine("第一次读取Customer数据");
Customer customer = _customerData.GetCustomerById(new Guid("DDF63750-3307-461B-B96A-7FF356540CB8"));
Console.WriteLine("修改读取的customer对象的名字");
customer.NameAddress.CustomerName = strCustomerName;
Console.WriteLine("更新数据库");
_customerData.ISessionFactoryCacheUpdate(customer);
Console.WriteLine("重置Session,第二次读取Customer数据");
Customer c2 = _customerData.GetCustomerById2(new Guid("DDF63750-3307-461B-B96A-7FF356540CB8"));
Assert.AreEqual(c2.NameAddress.CustomerName, strCustomerName);
}
测试结果
在第一次查询数据时,由于一级、二级缓存中都不存在需要的数据,这时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及其对象的结果集将从缓存中删除。
显式启用缓存查询
一个例子:显式使用IQuery.SetCacheable(true)方法缓存查询结果,第二次查询相同条件时,直接从缓存查询中读取。
/// <summary>
/// 根据客户姓名进行模糊查询
/// </summary>
/// <param name="strName">查询条件</param>
/// <returns>满足条件的客户信息</returns>
public IList<Customer> SearchByName(string strName)
{
ISession session = NHibernateHelper.GetSession();
//from后面跟的是持久化类Customer而不是数据表名TB_Customer
return session.CreateQuery("from Customer as customer where customer.NameAddress.CustomerName like '%" + strName + "%'")
//显示启用缓存查询
.SetCacheable(true)
.List<Customer>();
}
/// <summary>
/// 重置Session根据客户姓名进行模糊查询
/// </summary>
/// <param name="strName">查询条件</param>
/// <returns>满足条件的客户信息</returns>
public IList<Customer> SearchByName2(string strName)
{
//重置session
ISession session = NHibernateHelper.ResetSession();
//from后面跟的是持久化类Customer而不是数据表名TB_Customer
return session.CreateQuery("from Customer as customer where customer.NameAddress.CustomerName like '%" + strName + "%'")
//显示启用缓存查询
.SetCacheable(true)
.List<Customer>();
}
单元测试
[TestMethod]
public void SearchByNameTest()
{
Console.WriteLine("第一次查询某数据,显式缓存查询结果");
IList<Customer> customers = _customerData.SearchByName("z");
Assert.AreEqual(, customers.Count);
Console.WriteLine("重置session");
Console.WriteLine("第二次查询某数据,显式缓存查询结果");
IList<Customer> customers2 = _customerData.SearchByName2("z");
Assert.AreEqual(, customers.Count);
}
数据库中数据
测试结果
在第一次查询时,显示的缓存了查询的结果集,所以第二次查询的时候直接使用二级缓存中的数据了。
指定命名缓存区域
我们还可以使用.SetCacheRegion("cacheRegion")给查询缓存指定了特定的命名缓存区域,该查询缓存的缓存策略将由二级缓存的命名区域负责:
/// <summary>
/// 根据客户姓名进行模糊查询
/// </summary>
/// <param name="strName">查询条件</param>
/// <returns>满足条件的客户信息</returns>
public IList<Customer> SearchByName(string strName)
{
ISession session = NHibernateHelper.GetSession();
//from后面跟的是持久化类Customer而不是数据表名TB_Customer
return session.CreateQuery("from Customer as customer where customer.NameAddress.CustomerName like '%" + strName + "%'")
//显示启用缓存查询
.SetCacheable(true)
//指定缓存区域
.SetCacheRegion("querycache")
.List<Customer>();
}
/// <summary>
/// 重置Session根据客户姓名进行模糊查询
/// </summary>
/// <param name="strName">查询条件</param>
/// <returns>满足条件的客户信息</returns>
public IList<Customer> SearchByName2(string strName)
{
//重置session
ISession session = NHibernateHelper.ResetSession();
//from后面跟的是持久化类Customer而不是数据表名TB_Customer
return session.CreateQuery("from Customer as customer where customer.NameAddress.CustomerName like '%" + strName + "%'")
//显示启用缓存查询
.SetCacheable(true)
//指定缓存区域
.SetCacheRegion("querycache")
.List<Customer>();
}
第一次查询出来的结果集被存储在名为queryCache的缓存区域,第二次同样在这个缓存区域里寻找需要数据,如果第二次没有指定或者指定别的缓存区域则没有需要的数据,就要到数据库中查询了。(测试结果与上面的相同,就不再贴图)。
命名查询
可以在映射文件中定义命名查询,<query>元素提供了很多属性,可以用于缓存结果,在Customer.hbm.xml映射文件中定义名为selectCustomer的查询并查询所有Customer并启用缓存查询。配置如下
<!--query节点位于class节点外,hibernate-mapping节点内,不然会出现MappingException: Named query not known异常-->
<query cacheable="true" cache-mode="normal" name="selectCustomer">
<!--此时使用Customer是持久化类,而不是数据库表名称-->
from Customer
</query>
CustomerData类中添加如下的方法,用于测试:
1 /// <summary>
2 /// 使用命名查询的方式
3 /// </summary>
4 /// <returns></returns>
5 public IList<Customer> NamedQueryCache()
6 {
7 ISession session = NHibernateHelper.GetSession();
8 return session.GetNamedQuery("selectCustomer").List<Customer>();
9 }
10 /// <summary>
11 /// 重置session后使用命名查询的方式
12 /// </summary>
13 /// <returns></returns>
14 public IList<Customer> NamedQueryCache2()
15 {
16 ISession session = NHibernateHelper.ResetSession();
17 return session.GetNamedQuery("selectCustomer").List<Customer>();
18 }
单元测试
[TestMethod]
public void NamedQueryCache()
{
Console.WriteLine("--->第一次使用命名查询");
IList<Customer> c1 = _customerData.NamedQueryCache();
Assert.AreEqual(, c1.Count);
Console.WriteLine("--->第二次使用命名查询");
IList<Customer> c2 = _customerData.NamedQueryCache2();
Assert.AreEqual(, c2.Count);
}
运行测试,测试结果
NHibernate提供的查询(HQL、条件查询、原生SQL查询)类似,不再重复举例。
二级缓存管理
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:向二级缓存写数据,想不从二级缓存读数据,可以通过在nhibernate配置文件设置cache.use_minimal_puts从数据库中读取数据时,强制二级缓存刷新
ISessionFactory提供了一系列的EvictXXX()方法从二级缓存中删除一个实例。
一个例子:在第一次读取持久化实例时,结果集保存在二级缓存中,使用Evict方法从二级缓存中删除所有持久化实例,第二次查询相同数据,二级缓存中不存在则重新从数据库中查询。
在CustomerData类中添加如下方法
/// <summary>
/// 使用SessionFactory的Evict方法管理二级缓存
/// </summary>
public void SessionFactoryEvict(Type type)
{
ISessionFactory sessionFactoty = NHibernateHelper.GetSessionFactory();
sessionFactoty.Evict(type);
//也可以sessionFactoty.EvictEntity("Wolfy.Shop.Domain.Entities.Customer");
}
单元测试
[TestMethod]
public void ISessionFactoryCacheManageTest()
{
Console.WriteLine("第一次加载两个customer实例");
Customer customer1 = _customerData.GetCustomerById(new Guid("DDF63750-3307-461B-B96A-7FF356540CB8"));
Customer customer2 = _customerData.GetCustomerById(new Guid("095659B0-8D3F-4DC3-8861-9D7D8A9BA570"));
Console.WriteLine("从二级缓存中删除Customer类所有实例");
_customerData.SessionFactoryEvict(typeof(Customer));
Console.WriteLine("重置session并进行第二次读取持久化实例");
Customer customer3 = _customerData.GetCustomerById2(new Guid("DDF63750-3307-461B-B96A-7FF356540CB8"));
Assert.IsNotNull(customer3);
}
运行测试,测试结果
因为二级缓存是共享的,将ISession重置后,一级缓存也将跟ISession重置,第二次查询的时候,缓存中已经没有对象了,又从数据库中查询了一次。
强制刷新缓存区域
使用ISession提供的.SetCacheMode(CacheMode.Refresh) 方法可以强制刷新缓存区域,这样可以避免数据不一致问题。
1 /// <summary>
2 /// 根据客户姓名进行模糊查询
3 /// </summary>
4 /// <param name="strName">查询条件</param>
5 /// <returns>满足条件的客户信息</returns>
6 public IList<Customer> SearchByName(string strName)
7 {
8 ISession session = NHibernateHelper.GetSession();
9 //from后面跟的是持久化类Customer而不是数据表名TB_Customer
10 return session.CreateQuery("from Customer as customer where customer.NameAddress.CustomerName like '%" + strName + "%'")
11 //显示启用缓存查询
12 .SetCacheable(true)
13 //指定缓存区域
14 .SetCacheRegion("querycache")
15 //强制刷新缓存区域
16 .SetCacheMode(CacheMode.Refresh)
17 .List<Customer>();
18 }
19 /// <summary>
20 /// 重置Session根据客户姓名进行模糊查询
21 /// </summary>
22 /// <param name="strName">查询条件</param>
23 /// <returns>满足条件的客户信息</returns>
24 public IList<Customer> SearchByName2(string strName)
25 {
26 //重置session
27 ISession session = NHibernateHelper.ResetSession();
28 //from后面跟的是持久化类Customer而不是数据表名TB_Customer
29 return session.CreateQuery("from Customer as customer where customer.NameAddress.CustomerName like '%" + strName + "%'")
30 //显示启用缓存查询
31 .SetCacheable(true)
32 //指定缓存区域
33 .SetCacheRegion("querycache")
34 //强制刷新缓存区域
35 .SetCacheMode(CacheMode.Refresh)
36 .List<Customer>();
37 }
测试
[TestMethod]
public void SearchByNameTest2()
{
Console.WriteLine("第一次查询某数据,显式缓存查询结果");
IList<Customer> customers = _customerData.SearchByName("z");
Assert.AreEqual(, customers.Count);
Console.WriteLine("重置session");
Console.WriteLine("第二次查询某数据,显式缓存查询结果");
Console.WriteLine("----指定特定的命名缓存区域并强制刷新缓存区域----");
IList<Customer> customers2 = _customerData.SearchByName2("z");
Assert.AreEqual(, customers.Count);
}
测试结果
通过测试结果,也看出在第二次查询的时候,因为强制刷新了缓存区域,所以又重新访问数据库加载了一次数据。
总结
关于二级缓存的使用方法就介绍到这里,本篇文章也算是nhibernate入门系列的结尾篇了,关于nhibernate的高级功能,只能在遇到的时候总结,然后加入本系列了。
系列文章所有demo下载连接:链接:http://pan.baidu.com/s/1sjJja6x 密码:r9vh
参考文章
http://www.cnblogs.com/lyj/archive/2008/11/28/1343418.html
[Nhibernate]二级缓存(二)的更多相关文章
- [Nhibernate]二级缓存(一)
目录 写在前面 文档与系列文章 二级缓存 Nhibernate二级缓存提供程序 一个例子 总结 写在前面 上篇文章介绍了nhibernate中一级缓存的相关内容,一级缓存过期时间和ISession对象 ...
- 01-08-05【Nhibernate (版本3.3.1.4000) 出入江湖】NHibernate二级缓存:第三方MemCache缓存
一.准备工作 [1]根据操作系统(位数)选择下载相应版本的MemCache, MemCache的下载和安装,参看: http://www.cnblogs.com/easy5weikai/p/37606 ...
- [Nhibernate]二级缓存
[Nhibernate]二级缓存 目录 写在前面 文档与系列文章 二级缓存 Nhibernate二级缓存提供程序 一个例子 总结 写在前面 上篇文章介绍了nhibernate中一级缓存的相关内容,一级 ...
- 基于NHibernate二级缓存的MongoDB组件
设计一套基于NHibernate二级缓存的MongoDB组件(上) 摘要:NHibernate Contrib 支持很多第三方的二级缓存,如SysCache,MemCache,Prevalence ...
- NHibernate二级缓存(第十一篇)
NHibernate二级缓存(第十一篇) 一.NHibernate二级缓存简介 NHibernate由ISessionFactory创建,可以被所有的ISession共享. 注意NHibernate查 ...
- mybatis一级缓存和二级缓存(二)
注意事项与示例配置 一级缓存 Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个SqlSession而言.所以在参数和SQL完全一样的情况下,我们使用 ...
- NHibernate教程(20)——二级缓存(上)
本节内容 引入 介绍NHibernate二级缓存 NHibernate二级缓存提供程序 实现NHibernate二级缓存 结语 引入 上一篇我介绍了NHibernate内置的一级缓存即ISession ...
- NHibernate系列文章九:NHibernate对象二级缓存上
摘要 NHibernate的二级缓存由SessionFactory管理,由所有Session共享. NHibernate缓存读取顺序: 首先从一级缓存中读取,如果一级缓存对象存在,则读取一级缓存对象并 ...
- NHibernate系列文章十:NHibernate对象二级缓存下
摘要 上一节对NHibernate二级缓存做了简单介绍,NHibernate二级缓存是由SessionFactory管理的,所有Session共享.这一节介绍二级缓存其他两个方面:二级缓存查询和二级缓 ...
随机推荐
- Ubuntu下Apache+SVN+submin实现WEB管理SVN
为什么需要submin管理SVN? 原来在Ubuntu下,都是直接通过命令行创建SVN仓库并分配权限,但是这有一些问题: 每创建一个SVN仓库,都需要修改httpd.conf 每创建一个帐户,都需要手 ...
- JS导出PDF插件(支持中文、图片使用路径)
在WEB上想做一个导出PDF的功能,发现jsPDF比较多人推荐,遗憾的是不支持中文,最后找到pdfmake,很好地解决了此问题.它的效果可以先到http://pdfmake.org/playgroun ...
- android开发之onCreate( )方法详解
这里我们只关注一句话:This is where you should do all of your normal static set up.其中我们只关注normal static,normal: ...
- 关于java中的MANIFEST.MF 文件内容
打开Java的JAR文件我们经常可以看到文件中包含着一个META-INF目录, 这个目录下会有一些文件,其中必有一个MANIFEST.MF,这个文件描述了该Jar文件的很多信息.可以简化Java应用程 ...
- windows socket编程select模型使用
int select( int nfds, //忽略 fd_ser* readfds, //指向一个套接字集合,用来检测其可读性 ...
- UrlRewritingNet伪静态的使用方法与解决方案(URL重写)
在建站初期时,我们往往需要考虑的是使用真静态还是伪静态,这对于往后的站点配置,延展性都会产生深远的影响. 我使用伪静态的唯一目的:SEO优化.毋容置疑,伪静态在对于Spider是非常有利的,因此,我更 ...
- 用Lua扩展谷歌拼音输入法
谷歌拼音输入法最后一次更新是2013年,最近2年毫无动静,这个产品应该已经停了,不过这并不影响对它的使用,我一直喜欢它的简洁和稳定. 说不上来什么原因,忽然想起了摆弄摆弄谷歌拼音输入法的扩展特性(我经 ...
- The Engine Document of JustWeEngine
JustWeEngine - Android FrameWork An easy open source Android Native Game FrameWork. Github Game core ...
- 【2016-11-1】【坚持学习】【Day16】【MongoDB】【复制集 分片】
Mongodb 两种集群方式 复制集 通常是一主一从,一主多从 mongodb的复制至少需要两个节点.其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据. mongodb各 ...
- Django 1.9 支持中文(转)
昨天Django1.9发布了,今天我才刚开始学习Django,然后有一个问题就卡住了——如何支持中文?上网上查了好多资料都不好使,最后我搜索Django文件夹才发现,在1.9版本里,简体中文代码是zh ...