目录

写在前面

文档与系列文章

更新数据

二级缓存管理

总结

写在前面

本篇文章也算nhibernate入门系列的结尾了,在总结nhibernate系列的过程中,遇到了很多问题,学习的过程也是解决bug的过程,在学习nhibernate的过程中还学习了单元测试的使用,这个是附属产品,我也没有想到,算是意外收获吧。这个系列学完了,正好公司有个项目,马上就要立项,正好能将学到的东西运用到实际的项目中,想想就让人激动。当然,我相信在实际的项目中,肯定还会遇到问题,但是通过本系列的学习,我已经不怕遇到bug了,解决bug的过程也是成长。废话不多说了,进入本篇学习的内容吧。这篇继续上一篇NHibernate二级缓存剩下的内容,比如你修改、删除数据时,二级缓存是什么策略呢?我们如果使用缓存查询呢?如何管理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]二级缓存(一)

更新数据

在启用二级缓存时候,我们将数据查询出来后,如果对数据进行了修改,再查看二级缓存中的数据,此时的数据是什么呢?

一个例子

修改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]二级缓存(二)的更多相关文章

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

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

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

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

  3. [Nhibernate]二级缓存

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

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

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

  5. NHibernate二级缓存(第十一篇)

    NHibernate二级缓存(第十一篇) 一.NHibernate二级缓存简介 NHibernate由ISessionFactory创建,可以被所有的ISession共享. 注意NHibernate查 ...

  6. mybatis一级缓存和二级缓存(二)

    注意事项与示例配置 一级缓存 Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个SqlSession而言.所以在参数和SQL完全一样的情况下,我们使用 ...

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

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

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

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

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

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

随机推荐

  1. 003.安装nginx(lnmp)

    一.下载nginx 下载nginx源码包,解压: [root@huh ~]# cd /usr/local/src/ [root@huh src]# wget http://nginx.org/down ...

  2. linux 搜索相关命令(2)

    文件搜索相关命令 1:locate命令 需要 yum install mlocate locate 文件名 在后台数据库中按文件名搜索,搜索速度更快 /var/lib/mlocate #locate命 ...

  3. EF6 如何判断DataContext有修改,以及如何放弃修改

      如何判断DataContext有修改: EF6的 using (var db = new Model1()) { if (db.ChangeTracker.HasChanges()) { Cons ...

  4. Comparison of SQL Server Compact, SQLite, SQL Server Express and LocalDB

    Information about LocalDB comes from here and SQL Server 2014 Books Online. LocalDB is the full SQL ...

  5. Linux吃掉我的内存

    在Windows下资源管理器查看内存使用的情况,如果使用率达到80%以上,再运行大程序就能感觉到系统不流畅了,因为在内存紧缺的情况下使用交换分区,频繁地从磁盘上换入换出页会极大地影响系统的性能.而当我 ...

  6. JS入门学习,写一个简单的选项卡

    /* 经过昨天一整天的纠结和摸索.总结下学习初期我最致命的几个问题…… 1.var oDiv = document.getElementById('');    一定要多输,熟悉后o u什么的字母别搞 ...

  7. hibernate inverse属性的作用

    hibernate配置文件中有这么一个属性inverse,它是用来指定关联的控制方的.inverse属性默认是false,若为false,则关联由自己控制,若为true,则关联由对方控制.见例子: 一 ...

  8. POJ1201 Intervals[差分约束系统]

    Intervals Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 26028   Accepted: 9952 Descri ...

  9. POJ3928Ping pong[树状数组 仿逆序对]

    Ping pong Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 3109   Accepted: 1148 Descrip ...

  10. highcharts基本配置和使用highcharts做手机端图标

    使用highcharts三个理由:1>手机适配2>大数据的支持3>svg的优势缺点:不开源.学习资料少 官方有基本的常规用法,一般都是基于jquery写的例子,也可以自己封装函数,用 ...