首页 > 代码库 > 01-07-01【Nhibernate (版本3.3.1.4000) 出入江湖】并发控制

01-07-01【Nhibernate (版本3.3.1.4000) 出入江湖】并发控制

Nhibernate 并发控制

【1】悲观并发控制

       正在使用数据的操作,加上锁,使用完后解锁释放资源。

使用场景:数据竞争激烈,锁的成本低于回滚事务的成本

缺点:阻塞,可能死锁

【2】乐观并发控制:

       所谓乐观,就是乐观的认为其他人没有在用该资源,资源的使用者不加锁。

      A 读取数据后,如果该数据被别人B修改,产生错误,A回滚事务并重新开始。

使用场景:数据竞争不激烈,回滚事务的成本低于锁的成本。

 

-----------------------------------------------------------------------------------------------------------

Nhibernate 支持乐观并发控制:

  通过使用<version>或<timestamp>,节点配置。

  注意: <version>要配置一定在<Id>节点之后。否则抛出配置错误。如下所示:

Customer.hbm.xmml

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" 
                   namespace="Model" 
                   assembly="Model" 
                    default-lazy="true">
  
  <class name="Model.Customer, Model"
         table="Customer"
         discriminator-value=http://www.mamicode.com/"0" lazy="false">
    <!--unsaved-value=http://www.mamicode.com/"0" 主键表中不需要定义,而是需要在子表中定义-->
    <id name="CustomerId"
        column="CustomerId"
        type="int" 
        unsaved-value="http://www.mamicode.com/0">
      <generator class="native" />
      <!-- unsaved-value used to be null and generator was increment in h2.0.3 -->
    </id>

    <!--version标签必须放在Id后面,否则出错-->
    <version name="Version"/>
......

 

Order.hbm.xml

  <class name="Model.Order, Model" 
         table ="[Order]"
         discriminator-value=http://www.mamicode.com/"0" lazy="true">
    <id name="OrderId"
        column="OrderId"
        type="int" 
        unsaved-value="http://www.mamicode.com/0">
 <!--     unsaved-value="http://www.mamicode.com/0">-->
      <generator class="native" />
      <!-- unsaved-value used to be null and generator was increment in h2.0.3 -->
    </id>
    
    <!--version标签必须放在Id后面,否则出错-->
    <version name="Version"/>

-------------------------------------------------------------------

 

使用<version>标签 配置,实现并发控制。

   原理:检查数据库的Version字段的版本值:插入前检查本身携带的Version是否等于现在数据库的Version值,如果

    【1】相等,插入成功,并且Vesion加保存带数据库。

    【2】不相等,插入失败,并且抛出NHibernate.StaleObjectStateException类型异常,抛异常:所以并不需要Nhibernate事务的支持,用来回滚事务,因为根本就没插入。

                    

 

   -------------------------------------更新(Update)并发测试------------------------------------------

 

Customer更新(Update)并发测试:

 

       [TestMethod]
       [ExpectedException(typeof(NHibernate.StaleObjectStateException))]
       public void TestVessionByUpdate()
       {
           CustomerService customerService = new CustomerService();
           OrderService orderService = new OrderService();

           Customer customer = new Customer()
           {
               FirstName = "Test",
               LastName = "TestVessionByUpdate",
               Age = 10
           };
           customerService.Add(customer);

           Customer customer1 = customerService.Get(customer.CustomerId);
           Customer customer2 = customerService.Get(customer.CustomerId);

           string customer1Name = "customer1";
           customer1.FirstName = customer1Name;
           customer2.FirstName = "customer2Test";

           customerService.Update(customer2);  //能保存到数据库,Customer的version加1
           customerService.Update(customer1);  //抛出异常NHibernate.StaleObjectStateException,更新失败。
 }

 

Order更新(Update)并发测试:

       [TestMethod]
       [ExpectedException(typeof(NHibernate.StaleObjectStateException))]
       public void TestVessionOrderByUpdateCustomer()
       {
           CustomerService customerService = new CustomerService();
           OrderService orderService = new OrderService();

           Customer customer = new Customer()
           {
               FirstName = "Test",
               LastName = "TestVessionOrderByUpdateCustomer",
               Age = 10
           };
           Order order1 = new Order()
           {
               OrderDate = DateTime.Now.AddMinutes(1),
               Customer = customer
           };

           customer.Orders.Add(order1);
           customerService.Add(customer);

           Customer customer1 = customerService.Get(customer.CustomerId);
           Customer customer2 = customerService.Get(customer.CustomerId);

           customer1.Orders.First<Order>().OrderDate = customer1.Orders.First<Order>().OrderDate.AddDays(1);
           customer2.Orders.First<Order>().OrderDate = customer2.Orders.First<Order>().OrderDate.AddYears(1);
           customerService.Update(customer2);  //Customer和Order都能保存到数据库,Order的version加1
           customerService.Update(customer1);  //抛出异常NHibernate.StaleObjectStateException,更新失败。
}

 

 注意:

        注意已经把已经事务的相关代码注释掉了是为说明上面的并发控制并不需要事务的支持。Upadate的代码如下:

        public void Update(Customer customer)
        {
            ISession session = _sessionManager.GetSession();
            //ITransaction transaction = session.BeginTransaction();

            try
            {
                session.Update(customer);
                session.Flush();
                //transaction.Commit();
            }
            catch (Exception)
            {
                //transaction.Rollback();
                throw;
            }
            finally
            {
                session.Close();
            }
        }

 

    -------------------------------------删除(Update)并发测试------------------------------------------

如果启用了并发(即配置中添加了<version>标签属性),

那么删除的时候产生的SQL的Where语句会额外添加一个判断条件

           where customer.Version = 数据库里.vesesion

 

测试代码:

       [TestMethod]
       [ExpectedException(typeof(NHibernate.StaleObjectStateException))]
       public void TestVessionDeleteCustomer()
       {
           CustomerService customerService = new CustomerService();
           OrderService orderService = new OrderService();

           Customer customer = new Customer()
           {
               FirstName = "Test",
               LastName = "TestVessionDeleteCustomer",
               Age = 10
           };
           customerService.Add(customer);
           Assert.IsNotNull(customerService.Get(customer.CustomerId));


           Customer customer1 = customerService.Get(customer.CustomerId);
           Customer customer2 = customerService.Get(customer.CustomerId);

           customerService.Delete(customer1);
           customerService.Delete(customer2);

       }

 

如下图所示:

 

 

如果并发删除同一条记录,会抛出NHibernate.StaleObjectStateException 类型的异常。