zoukankan      html  css  js  c++  java
  • 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);
        }
    }

    输出结果:

    管理NHibernate二级缓存

    测试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二级缓存?我们合理使用缓存,可以大幅度地提高程序的性能。

  • 相关阅读:
    HAProxy、Keepalived 在 Ocatvia 的应用实现与分析
    Octavia 的 HTTPS 与自建、签发 CA 证书
    Octavia 创建 loadbalancer 的实现与分析
    OpenStack Rally 质量评估与自动化测试利器
    自建 CA 中心并签发 CA 证书
    Failed building wheel for netifaces
    通过 vSphere WS API 获取 vCenter Datastore Provisioned Space 置备空间
    OpenStack Placement Project
    我们建了一个 Golang 硬核技术交流群(内含视频福利)
    没有图形界面的软件有什么用?
  • 原文地址:https://www.cnblogs.com/zhengwei-cq/p/7388551.html
Copyright © 2011-2022 走看看