NHIbernate学习之旅【十】—— 缓存

  Nhibernate中的缓存分两种,一级缓存和二级缓存。

  首先一级缓存,也就是Isession缓存。是nhibernate内置的,生存周期为Isession周期。你第一次查询数据库Isession会保存这次查询的记录,如果你第二次查询这个数据时,Isession会先查看缓存,如果存在这个记录,就直接返回,不存在就查询数据库,并保存缓存再返回。

  

ExpandedBlockStart.gif代码
    Console.WriteLine("第一次读取持久化实例");
    Customer customer1 
= _transaction.GetCustomerById(1);
    Console.WriteLine(
"第二次读取持久化实例");
    Customer customer2 
= _transaction.GetCustomerById(1);
    Assert.AreSame(customer1, customer2);

 

 

  执行以上语句你会发现第一次查询了数据库,而第二次没有。

  

  就如前面所说,如果你用USING(_SESSION){}的话,把两个操作放在不同session里面,执行的话就会生存两条查询语句。

  一级缓存的管理:

  一级缓存包含一下几个常用方法:

  

  ISession.Evict(object):从缓存中删除指定实例。

  ISession.Clear():清空缓存。

  ISession.Contains(object):检查缓存中是否包含指定实例。

 

ExpandedBlockStart.gif代码
    //1.加载两个实例放入ISession缓存
    Customer customer1 = _transaction.GetCustomerById(1);
    Customer customer2 
= _transaction.GetCustomerById(2);
    
//2.加载实例后,缓存包含两个实例
    Assert.IsTrue(_session.Contains(customer1));
    Assert.IsTrue(_session.Contains(customer2));
    
//3.从缓存中删除Customer1实例
    _session.Evict(customer1);
    Assert.IsFalse(_session.Contains(customer1));
    Assert.IsTrue(_session.Contains(customer2));
    
//4.清空ISession缓存,实例不和缓存关联
    _session.Clear();
    Assert.IsFalse(_session.Contains(customer1));
    Assert.IsFalse(_session.Contains(customer2));

 

 

  以上是一级缓存很简单,接下来是二级。

  二级缓存是ISessionFactory级别的缓存,并且而缓存是可扩展的,即可以由第三方提供。

  我们这里只用nhibernate内置的测试。

  二级缓存默认是不启用的,我们要对其进行配置,完整配置如下:

 

ExpandedBlockStart.gif代码
<?xml version="1.0" encoding="utf-8"?>
<hibernate-configuration  xmlns="urn:nhibernate-configuration-2.2" >
    
<session-factory name="NHibernate.Test">
        
<property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
        
<property name="connection.connection_string">
            Data Source=.;Initial Catalog=NhibernateSample;Persist Security Info=True;User ID=sa;Password=sa
        
</property>
        
<property name="adonet.batch_size">10</property>
        
<property name="show_sql">false</property>
        
<property name="dialect">NHibernate.Dialect.MsSql2000Dialect</property>
        
<property name="use_outer_join">true</property>
        
<property name="command_timeout">60</property>
        
<property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>
        
<!--1.配置二级缓存提供程序-->
        
<property name="cache.provider_class">NHibernate.Cache.HashtableCacheProvider</property>
        
<!--2.显式启用二级缓存-->
        
<property name ="cache.use_second_level_cache">true</property>
        
<!--3.启动查询缓存-->
        
<property name="cache.use_query_cache">true</property>
        
<property name="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</property>
        
<mapping assembly="NHibernateSample.Domain"/>
        
<!--4.配置映射的二级缓存-->
        
<class-cache class="NHibernateSample.Domain.Entities.Customer,NHibernateSample.Domain" usage="read-write"/>
    
</session-factory>
</hibernate-configuration>

 

  这里要注意第四个一定要放在最后,不然会报错。

  第一条是:因为有第三方提供的,所以我们要先配置提供方,然后第二条启用,第三天开启查询缓存,第四你的数据库映射文件实体文件。

  以上是对全局的配置,我们还要对映射文件进行配置。

  

ExpandedBlockStart.gif代码
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
   assembly
="NHibernateSample.Domain"
   namespace
="NHibernateSample.Domain.Entities">

    
<class name ="Customer">

        
<cache usage="read-write"/>

        
<id name="Id" column ="CustomerId">
            
<generator class ="increment"/>
        
</id>
        
<version name="Version" column="Version" type="integer" unsaved-value="0"/>
            
<property name ="FirstName"/>
            
<property name ="LastName"/>
    
<set name="Orders" table="[Order]" generic="true" inverse="true">

          <cache usage="read-only"/>

          
<key column="Customer" foreign-key="FK_CustomerOrders"/>
          
<one-to-many class="NHibernateSample.Domain.Entities.Order,NHibernateSample.Domain"/>
    
</set>
    
<sql-insert>exec CustomerInsert ?,?,?,?</sql-insert>
    
<sql-delete>exec CustomerDelete ?,?</sql-delete>
    
</class>
    
<sql-query name="ParamSProcs">
        
<return-scalar column="value" type="long"/>
        
<return-scalar column="value2" type="long"/>
        exec paramSProcs ?, ?
    
</sql-query>
    
<sql-query name="EntitySProcs">
        
<return class="NHibernateSample.Domain.Entities.Customer,NHibernateSample.Domain"/>
        exec entitySProcs :customerId
    
</sql-query>
</hibernate-mapping>

 

  上面的usage可以配置4种属性值:  

read-only:只读缓存。适用于只读数据。可用于群集中。read-write:读写缓存。nonstrict-read-write:非严格读写缓存。不保证缓存与数据库的一致性。transactional:事务缓存。提供可重复读的事务隔离级别。

 

  配置好后还是用刚才的例子测试,你会发现,就算是两个独立的Isession操作,只要是一样的查询,只会查询一次。

  这是因为,我们开启了二级缓存,在第一次查询数据时,由于一级、二级缓存中都不存在需要的数据,这时NHibernate从数据库中查询数据。第二次读取同一数据,NHibernate首先从内置缓存(一级缓存)中查找是否存在所需要数据,由于不是在同一个ISession中,所以内置ISession缓存中不存在所需数据,NHibernate则查询二级缓存,这时由于第一次查询了这条数据,所以在二级缓存中存在所需数据,则直接使用缓存中数据。

  那如果是更新呢,缓存的数据也会更新吗?

 

        //重置session
        private void ResetSession()
        {
            
if (_session.IsOpen)
                _session.Close();
            _session 
= _helper.GetSession();
        }

 

  

 

ExpandedBlockStart.gif代码
    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则查询二级缓存,这时由于第一次查询了这条数据,所以在二级缓存中存在所需数据,则直接使用缓存中数据。这时缓存中的数据也是更新的。

  以上是缓存持久化类和集合,我们还可以缓存查询结果集,就是我们全局配置里面的<property name="cache.use_query_cache">true</property>

  不过有一点需要注意,这种缓存并不是永久有效,只要缓存的表更新了,缓存就消失。

  1.使用IQuery.SetCacheable(true)方法缓存查询结果,第二次查询相同条件时,直接从缓存查询中读取。

  

ExpandedBlockStart.gif代码
    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);
    }

 

 

  结果是:第二次直接使用缓存的。

  2.使用.SetCacheRegion("cacheRegion")给查询缓存指定了特定的命名缓存区域,该查询缓存的缓存策略将由二级缓存的命名区域负责:

  

ExpandedBlockStart.gif代码
    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的缓存区域,第二次同样在这个缓存区域里寻找需要数据,如果第二次没有指定或者指定别的缓存区域则没有需要的数据,就要到数据库中查询了。

  3.可以在映射文件中定义命名查询,<query>元素提供了很多属性,可以用于缓存结果:

  <query cacheable ="true" cache-mode="normal" name="selectCustomer">from Customer</query>

  

  Customer.hbm.xml映射文件中定义名为selectCustomer的查询由于查询所有Customer并启用缓存查询,缓存模式为默认方式.

  

ExpandedBlockStart.gif代码
    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二级缓存由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从数据库中读取数据时,强制二级缓存刷新

  例子就不写了,和一级差不多的,自己去试一下。

 

 

  结语:常用的nhibernate方法李永京都写了就这么多,这10篇文章写下来几乎全是抄袭李永京的吧,-_-!。自己也想写点原创的,但个人经验不足,写了又怕误人子弟,因此全部就照搬了,然后把李永京写的不全面的补上一点。当然整片写下了,大多数都是自己亲手测试了的,可以说自己可以配置和简单使用NHibernate了。这10篇文章估计也没多少人看,因为我没有推广到首页发布区或者勾选类别,因为全是抄袭的。。。,但是这篇文章还没完,还有最后一篇,接下来我会学习其他的内容了,设计模式,AOP,权限管理等等,把这些学完,我会自己动手把这些所学组合起来搭配一个框架,把这些抄袭的东西变成自己东西。但是不知道要等多长时间了呵呵,慢慢来吧只要有心去学。

                                  ~END~

转载于:https://www.cnblogs.com/neekey/archive/2010/05/22/1741507.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值