[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工具的使用(二)——创建表及其约束、存储过程、视图
一级缓存
关于缓存的详细内容可以查看nhibernate文档[NHibernate]缓存(NHibernate.Caches)。
NHibernate session有一个内部的(一级)缓存,存放着它的实体。这些缓存没有共享,因此session被销毁时它的缓存也被销毁了。
NHibernate提供了二级缓存系统;它在SessionFactory级别工作。因此它被同一个SessionFactory产生的session共享。
使用每个请求(request)一个session模式,很多Session可以并发的访问同一个实体,而不用每次都访问数据库,因此性能获得了提升。
可见一级缓存的过期时间是和session的生命周期相同的。
ISession实例创建后即可使用ISession缓存。此后,ISession实例操作数据时,首先查询内置缓存,如果ISession缓存中存在相应数据,则直接使用缓存数据。如果不存在,则查询数据库并把其结果存在缓存中。
为了方便测试,这里改用单元测试的方法进行(也顺便学习一下单元测试,说实话之前很少用这东西,自从使用之后,发现真是太方便了)。
一个例子
1、根据客户id查询符合条件的客户对象。
1 /// <summary> 2 /// 根据客户id查询 3 /// </summary> 4 /// <param name="customerID"></param> 5 /// <returns></returns> 6 public Customer GetCustomerById(Guid customerID) 7 { 8 ISession session = NHibernateHelper.GetSession(); 9 return session.Get<Customer>(customerID); 10 }
单元测试该方法
1 namespace Wolfy.DataUnitTest 2 { 3 [TestClass] 4 public class CustomerDataTest 5 { 6 CustomerData _customerData = null; 7 public CustomerDataTest() 8 { 9 _customerData = new CustomerData(); 10 } 11 [TestMethod] 12 public void GetCustomerByIdTest() 13 { 14 Console.WriteLine("第一次加载"); 15 Customer c1 = _customerData.GetCustomerById(new Guid("DDF63750-3307-461B-B96A-7FF356540CB8")); 16 Console.WriteLine("第二次加载"); 17 Customer c2 = _customerData.GetCustomerById(new Guid("DDF63750-3307-461B-B96A-7FF356540CB8")); 18 Assert.AreEqual(c1, c2); 19 } 20 } 21 }
测试结果
当第一次加载数据时,缓存中还没有该数据,则从数据库中查询并将查询的结果放入缓存。第二次查询同一个持久化实例时,缓存中已经存在该持久化实例,应用程序将直接从缓存中获取数据,而不必再次从数据库中读取数据,提高了查询效率。
2、分别从两个会话中查询Customer
构建测试用例,因为获得ISession实例采用的单例模式,如果不进行重置那么Session是同一个对象,所以这里需要在第一次查询后重置ISession,NhibernateHelper代码如下:
1 namespace Wolfy.Shop.Data 2 { 3 /// <summary> 4 /// 描述:nhibernate辅助类 5 /// 创建人:wolfy 6 /// 创建时间:2014-10-16 7 /// </summary> 8 public class NHibernateHelper 9 { 10 private static ISessionFactory _sessionFactory; 11 private static ISession _session; 12 private static object _objLock = new object(); 13 private NHibernateHelper() 14 { 15 16 } 17 /// <summary> 18 /// 创建ISessionFactory 19 /// </summary> 20 /// <returns></returns> 21 public static ISessionFactory GetSessionFactory() 22 { 23 if (_sessionFactory == null) 24 { 25 lock (_objLock) 26 { 27 if (_sessionFactory == null) 28 { 29 //配置ISessionFactory 30 _sessionFactory = (new Configuration()).Configure().BuildSessionFactory(); 31 } 32 } 33 } 34 return _sessionFactory; 35 36 } 37 /// <summary> 38 /// 重置Session 39 /// </summary> 40 /// <returns></returns> 41 public static ISession ResetSession() 42 { 43 if (_session.IsOpen) 44 _session.Close(); 45 _session = _sessionFactory.OpenSession(); ; 46 return _session; 47 } 48 /// <summary> 49 /// 打开ISession 50 /// </summary> 51 /// <returns></returns> 52 public static ISession GetSession() 53 { 54 _sessionFactory = GetSessionFactory(); 55 if (_session == null) 56 { 57 lock (_objLock) 58 { 59 if (_session == null) 60 { 61 _session = _sessionFactory.OpenSession(); 62 } 63 } 64 } 65 return _session; 66 } 67 } 68 69 }
修改GetCustomerById方法
1 /// <summary> 2 /// 根据客户id查询 3 /// </summary> 4 /// <param name="customerID"></param> 5 /// <returns></returns> 6 public Customer GetCustomerById(Guid customerID) 7 { 8 ISession session = NHibernateHelper.GetSession(); 9 return session.Get<Customer>(customerID); 10 } 11 /// <summary> 12 /// 根据客户id查询 13 /// </summary> 14 /// <param name="customerID"></param> 15 /// <returns></returns> 16 public Customer GetCustomerById2(Guid customerID) 17 { 18 ISession session = NHibernateHelper.ResetSession(); 19 return session.Get<Customer>(customerID); 20 }
单元测试
1 [TestMethod] 2 public void GetCustomerById2Test() 3 { 4 Console.WriteLine("Session1 第一次加载"); 5 Customer c1 = _customerData.GetCustomerById(new Guid("DDF63750-3307-461B-B96A-7FF356540CB8")); 6 Assert.IsNotNull(c1); 7 Console.WriteLine("Session2 第二次加载"); 8 Customer c2 = _customerData.GetCustomerById2(new Guid("DDF63750-3307-461B-B96A-7FF356540CB8")); 9 Assert.IsNotNull(c2); 10 }
运行测试,结果
由上测试可以看出,在两个会话中获取同一持久化实例时,两个会话的缓存是独立的,一个会话的数据操作不会影响到另外一个会话。
从结果我们可以说明虽然这两个会话读取的是同一个实例,但需要至少两次数据库操作(关联的数据表除外),从而说明了Session缓存不是共享的,一个Session的缓存内容只有在本Session实例范围内可用。
3、ISession.Get()和ISession.Load()比较
分别使用Get和Load方法查询某个客户信息。
1 /// <summary> 2 /// 根据客户id查询 3 /// </summary> 4 /// <param name="customerID"></param> 5 /// <returns></returns> 6 public Customer GetCustomerById(Guid customerID) 7 { 8 ISession session = NHibernateHelper.GetSession(); 9 return session.Get<Customer>(customerID); 10 } 11 public Customer LoadCustomerById(Guid customerID) 12 { 13 ISession session = NHibernateHelper.GetSession(); 14 return session.Load<Customer>(customerID); 15 }
单元测试
1 [TestMethod] 2 public void GetCustomerByIdTest() 3 { 4 Console.WriteLine("----使用Get方式获取Customer实例----"); 5 Customer c1 = _customerData.GetCustomerById(new Guid("DDF63750-3307-461B-B96A-7FF356540CB8")); 6 Assert.IsNotNull(c1); 7 Console.WriteLine("输出该客户的id信息:"); 8 Console.WriteLine(c1.CustomerID); 9 Assert.AreEqual(c1.CustomerID.ToString().ToUpper(), "DDF63750-3307-461B-B96A-7FF356540CB8"); 10 Console.WriteLine("输出该客户的名字:"); 11 Console.WriteLine(c1.NameAddress.CustomerName); 12 Assert.AreEqual(c1.NameAddress.CustomerName,"wolfy"); 13 } 14 [TestMethod] 15 public void LoadCustomerByIdTest() 16 { 17 Console.WriteLine("----使用Load方式获取Customer实例----"); 18 Customer c1 = _customerData.LoadCustomerById(new Guid("DDF63750-3307-461B-B96A-7FF356540CB8")); 19 Assert.IsNotNull(c1); 20 Console.WriteLine("输出该客户的id信息:"); 21 Console.WriteLine(c1.CustomerID); 22 Assert.AreEqual(c1.CustomerID.ToString().ToUpper(), "DDF63750-3307-461B-B96A-7FF356540CB8"); 23 Console.WriteLine("输出该客户的名字:"); 24 Console.WriteLine(c1.NameAddress.CustomerName); 25 Assert.AreEqual(c1.NameAddress.CustomerName, "wolfy"); 26 }
运行单元测试,查看结果
使用ISession.Get()方法立即把对象实例保存到缓存中,使用ISession.Load()方法当你需要使用的时候再访问数据库把这个实例保存在缓存中(有点懒加载的意思,关于Lazy加载可参考前面的文章,Load方法默认是使用Lazy方式加载的)。
一级缓存管理
ISession接口为我们提供了一些常用方法来显式管理一级缓存:
ISession.Evict(object):从缓存中删除指定实例。
ISession.Clear():清空缓存。
ISession.Contains(object):检查缓存中是否包含指定实例。
在CustomerData中添加方法
1 /// <summary> 2 /// 清除session中的缓存 3 /// </summary> 4 public void ClearCache() 5 { 6 ISession session = NHibernateHelper.GetSession(); 7 session.Clear(); 8 } 9 /// <summary> 10 /// 从缓存中移除某个对象 11 /// </summary> 12 /// <param name="customer"></param> 13 /// <returns></returns> 14 public void RemoveCustomerFromCache(Customer customer) 15 { 16 ISession session = NHibernateHelper.GetSession(); 17 session.Evict(customer); 18 }
1 /// <summary> 2 /// session是否包含customer对象 3 /// </summary> 4 /// <param name="customer"></param> 5 /// <returns></returns> 6 public bool IsContains(Customer customer) 7 { 8 ISession session = NHibernateHelper.GetSession(); 9 return session.Contains(customer); 10 }
单元测试方法
1 [TestMethod] 2 public void ManagerCacheTest() 3 { 4 //1.向ISession中添加两个Customer对象并缓存 5 Customer customer1 = _customerData.GetCustomerById(new Guid("DDF63750-3307-461B-B96A-7FF356540CB8")); 6 Customer customer2 = _customerData.GetCustomerById(new Guid("095659B0-8D3F-4DC3-8861-9D7D8A9BA570")); 7 //2.加载实例后,缓存包含两个实例 8 Assert.IsTrue(_customerData.IsContains(customer1)); 9 Assert.IsTrue(_customerData.IsContains(customer2)); 10 //3.从缓存中删除Customer1实例 11 _customerData.RemoveCustomerFromCache(customer1); 12 Assert.IsFalse(_customerData.IsContains(customer1)); 13 Assert.IsTrue(_customerData.IsContains(customer2)); 14 //4.清空ISession缓存,实例不和缓存关联 15 _customerData.ClearCache(); 16 Assert.IsFalse(_customerData.IsContains(customer1)); 17 Assert.IsFalse(_customerData.IsContains(customer2)); 18 }
测试结果
该测试,首先加载两个customer实例,此时已将它们都存入一级缓存,首先使用RemoveCustomerFromCache方法,从缓存中将customer1对象移除,然后使用ClearCache方法清空缓存。
总结
本篇文章就到这里,一级缓存是和ISession关联的,多个Session的缓存是不能共享的。本篇文章使用了单元测试的方式进行测试,也是首次使用单元测试,也稍微研究了一下,关于单元测试的东西,算是附加学习的吧。
参考文章
-
博客地址:http://www.cnblogs.com/wolf-sun/
博客版权:如果文中有不妥或者错误的地方还望高手的你指出,以免误人子弟。如果觉得本文对你有所帮助不如【推荐】一下!如果你有更好的建议,不如留言一起讨论,共同进步! 再次感谢您耐心的读完本篇文章。