本节内容
呵呵,今天收到了微软 "最有影响力开发者" 礼包,很高兴自己 (详情请进),这篇我还继续上一篇的话题聊聊 NHibernate 二级缓存剩下的内容,比如你修改、删除数据时,二级缓存是什么策略呢?我们如果使用缓存查询呢?如何管理 NHibernate 二级缓存呢?
不知道具体配置的请转到观看上一篇的内容,这篇我们再写几个测试,来看看 NHibernate 二级缓存一些细节:
当我们启用二级缓存时,如果第一次把数据查询出来,然后修改了这个数据,这时二级缓存中的数据是什么呢?我们写一个测试看看究竟吧:
- [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 及其对象的结果集将从缓存中删除。
这个例子显式使用 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);
- }
- }
看看结果
由于我们显式缓存查询结果,在第二次查询时,直接使用二级缓存中的结果集。
我们还可以使用. 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 的缓存区域,第二次同样在这个缓存区域里寻找需要数据,如果第二次没有指定或者指定别的缓存区域则没有需要的数据,就要到数据库中查询了。
可以在映射文件中定义命名查询,<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 二级缓存由 ISessionFactory 创建并由 ISessionFactory 自行维护。我们使用 NHibernate 操作数据时,ISessionFactory 能够自动同步缓存,保证缓存的有效性。但是当我们批量操作数据时,往往 NHibernate 不能维护缓存持久有效。ISessionFactory 提供了可编程方式的缓存管理方法。
ISessionFactory 提供了一系列的 EvictXXX() 方法可以方便的从二级缓存中删除一个实例、删除一个集合、一个命名缓存等操作
ISession 内置缓存可以共享 ISessionFactory 缓存,通过指定 ISession 的 CacheMode 可以控制 ISession 和 ISessionFactory 的交互方式。ISession 可以通过以下五种方式和 ISessionFactory 交互:
我们可以使用 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);
- }
- }
输出结果:
我们使用 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 二级缓存?我们合理使用缓存,可以大幅度地提高程序的性能。
来源: http://lib.csdn.net/article/dotnet/39130