NHibernate之旅(7):初探NHibernate中的并发控制

本节内容

  • 什么是并发控制?

    • 悲观并发控制(Pessimistic Concurrency)
    • 乐观并发控制(Optimistic Concurrency)
  • NHibernate支持乐观并发控制
  • 实例分析
  • 结语

什么是并发控制?

当许多人试图同时修改数据库中的数据时,必须实现一个控制系统,使一个人所做的修改不会对他人所做的修改产生负面影响。这称为并发控制。

简单的理解就是2个或多个用者同时编辑相同的数据。这里的用者可能是:实际用户、不同服务、不同的代码段(使用多线程),及其在断开式和连接式情况下可能发生的情况。

并发控制理论根据建立并发控制的方法而分为两类:

悲观并发控制(Pessimistic Concurrency)

一个锁定系统,可以阻止用户以影响其他用户的方式修改数据。如果用户执行的操作导致应用了某个锁,只有这个锁的所有者释放该锁,其他用户才能执行与该锁冲突的操作。这种方法之所以称为悲观并发控制,是因为它主要用于数据争用激烈的环境中,以及发生并发冲突时用锁保护数据的成本低于回滚事务的成本的环境中。

简单的理解通常通过“独占锁”的方法。获取锁来阻塞对于别的进程正在使用的数据的访问。换句话说,读者和写者之间是会互相阻塞的 ,这可能导致数据同步冲突。

乐观并发控制(Optimistic Concurrency)

在乐观并发控制中,用户读取数据时不锁定数据。当一个用户更新数据时,系统将进行检查,查看该用户读取数据后其他用户是否又更改了该数据。如果其他用户更新了数据,将产生一个错误。一般情况下,收到错误信息的用户将回滚事务并重新开始。这种方法之所以称为乐观并发控制,是由于它主要在以下环境中使用:数据争用不大且偶尔回滚事务的成本低于读取数据时锁定数据的成本。

(以上摘自SQL Server2008 MSDN文档)

NHibernate支持乐观并发控制

NHibernate提供了一些方法来支持乐观并发控制:在映射文件中定义了<version> 节点和<timestamp>节点。其中<version> 节点用于版本控制,表明表中包含附带版本信息的数据。<timestamp>节点用于时间截跟踪,表明表中包含时间戳数据。时间戳本质上是一种对乐观锁定不是特别安全的实现。但是通常而言,版本控制方式是首选的方法。当然,有时候应用程序可能在其他方面使用时间戳。

下面用两幅图显示这两个节点映射属性:

看看它们的意义:

  • access(默认为property):NHibernate用于访问特性值的策略。
  • column(默认为特性名):指定持有版本号的字段名或者持有时间戳的字段名。
  • generated:生成属性,可选never和always两个属性。
  • name:持久化类的特性名或者指定类型为.NET类型DateTime的特性名。
  • type(默认为Int32):版本号的类型,可选类型为Int64、Int32、Int16、Ticks、Timestamp、TimeSpan。注意:<timestamp>和<version type="timestamp">是等价的。
  • unsaved-value(在版本控制中默认是“敏感”值,在时间截默认是null):表示某个实例刚刚被实例化(尚未保存)时的版本特性值,依靠这个值就可以把这种情况和已经在先前的会话中保存或装载的游离实例区分开来。(undefined指明使用标识特性值进行判断)

实例分析

下面用一个例子来实现乐观并发控制,这里使用Version版本控制。

1.修改持久化Customer类:添加Version属性

public class Customer
{
    public virtual int CustomerId { get; set; }
    //版本控制
    public virtual int Version { get; set; }
    public virtual string Firstname { get; set; }
    public virtual string Lastname { get; set; }
}

2.修改映射文件:添加Version映射节点

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
   assembly="DomainModel" namespace="DomainModel">
  <class name ="DomainModel.Entities.Customer,DomainModel" table="Customer">
    <id name="CustomerId" column="CustomerId" type="Int32" unsaved-value="0">
      <generator class ="native"></generator>
    </id>
    <version name="Version" column="Version" type="integer" unsaved-value="0"/>
    <property name="Firstname" column ="Firstname" type="string" length="50" not-null="false"/>
    <property name ="Lastname" column="Lastname" type="string" length="50" not-null="false"/>
  </class>
</hibernate-mapping>

3.修改数据库,添加Version字段

具体参数:[Version] [int] NOT NULL 默认值为1,当然了修改数据库是最原始的方式了,如果你会使用SchemaExport,可以直接利用持久化类和映射文件生成数据库,以后在介绍如何使用这个。

4.并发更新测试

在测试之前,我们先看看数据库中什么数据,预知一下:

编写并发更新测试代码:

查询2次CustomerId为1的客户,这里就是上面的第一条数据,第一个修改为"CnBlogs",第二个修改为"www.cnblogs.com",两者同时更新提交。你想想发生什么情况?

[Test]
public void UpdateConcurrencyViolationCanotThrowException()
{
    Customer c1 = _transaction.GetCustomerById(1);
    Customer c2 = _transaction.GetCustomerById(1);
    c1.Name.Firstname = "CnBlogs";
    c2.Name.Firstname = "www.cnblogs.com";

    _transaction.UpdateCustomerTransaction(c1);
    _transaction.UpdateCustomerTransaction(c2);
}

让我们去看看数据库吧,一目了然:

我们发现CustomerId为1的客户更新了FirstName数据,并且Version更新为2。你知道什么原理了吗?看看这步NHibernate生成的SQL语句(我的可能比你的不一样):先查询数据库,在直接更新数据,看看NHibernate多么实在,明显做了一些优化工作。

SELECT customer0_.CustomerId as CustomerId3_0_,
customer0_.Version as Version3_0_,
customer0_.Firstname as Firstname3_0_,
customer0_.Lastname as Lastname3_0_,
customer0_1_.OrderDiscountRate as OrderDis2_4_0_,
customer0_1_.CustomerSince as Customer3_4_0_,
case
    when customer0_1_.CustomerId is not null then 1
    when customer0_.CustomerId is not null then 0
end
as clazz_0_ FROM Customer
customer0_ left outer join PreferredCustomer customer0_1_
on customer0_.CustomerId=customer0_1_.CustomerId
WHERE customer0_.CustomerId=@p0; @p0 = ‘1‘

UPDATE Customer SET Version = @p0, Firstname = @p1, Lastname = @p2
WHERE CustomerId = @p3 AND Version = @p4;
@p0 = ‘2‘, @p1 = ‘www.cnblogs.com‘, @p2 = ‘Lee‘, @p3 = ‘1‘, @p4 = ‘1‘

5.并发删除测试

我们再来编写一个测试用于并发删除。查询2次CustomerId为2的客户,这里就是上面的第二条数据,两者同时删除数据。你想想发生什么情况?

[Test]
[ExpectedException(typeof(NHibernate.StaleObjectStateException))]
public void DeleteConcurrencyViolationCanotThrowException()
{
    Customer c1 = _transaction.GetCustomerById(2);
    Customer c2 = _transaction.GetCustomerById(2);

    _transaction.DeleteCustomerTransaction(c1);
    _transaction.DeleteCustomerTransaction(c2);
}

同理,看看数据库里的数据,第二条数据不见了。

其生成SQL的查询语句同上面一样,只是一条删除语句:

DELETE FROM Customer WHERE CustomerId = @p0 AND Version = @p1; @p0 = ‘2‘, @p1 = ‘1‘

好了,这里通过两个简单的实例说明了在NHibernate中对并发控制的支持。相信有了一定的了解,大家也可以编写一些有趣的测试来试试NHibernate中的乐观并发控制。

结语

这一篇我们初步探索了NHibernate中的并发控制,并用一个典型的实例分析了具体怎么做。我想这只是蜻蜓点水,更多的乐趣就自己探索吧。比如在不同的Session中的并发啊,更新啊,删除啊......

版权声明:本文为博主http://www.zuiniusn.com原创文章,未经博主允许不得转载。

时间: 2024-10-14 07:32:30

NHibernate之旅(7):初探NHibernate中的并发控制的相关文章

[转]NHibernate之旅(7):初探NHibernate中的并发控制

本节内容 什么是并发控制? 悲观并发控制(Pessimistic Concurrency) 乐观并发控制(Optimistic Concurrency) NHibernate支持乐观并发控制 实例分析 结语 什么是并发控制? 当许多人试图同时修改数据库中的数据时,必须实现一个控制系统,使一个人所做的修改不会对他人所做的修改产生负面影响.这称为并发控制. 简单的理解就是2个或多个用者同时编辑相同的数据.这里的用者可能是:实际用户.不同服务.不同的代码段(使用多线程),及其在断开式和连接式情况下可能

NHibernate之旅(18):初探代码生成工具使用

本节内容 引入 代码生成工具 结语 引入 我们花了大量的篇幅介绍了相关NHibernate的知识.一直都是带着大家手动编写代码,首先创建数据库架构.然后编写持久化类和映射文件,最后编写数据操作方法.測试方法. 这是典型的数据库驱动开发(DbDD,Database-Driven Developent)技术.可是自己不是这样做的,我先编写持久化类和映射文件,然后偷偷的使用SchemaExport工具把数据库生成了.按上面的步骤写文章的,关于SchemaExport工具就是下一篇的事情了,这篇说说利用

NHibernate之旅(17):探索NHibernate中使用存储过程(下)

本节内容 引入 实例分析 拾遗 结语 引入 上两篇,介绍使用MyGeneration提供的模板创建存储过程和删除对象.创建对象.更新对象整个详细过程,这篇介绍如何利用<sql-query>做更多的事,在程序开发中,我们不仅仅只利用存储过程增删查改对象,我们还可以想执行任意的存储过程,这不局限于某个对象,某个CURD操作,怎么做呢?注意:本篇并非官方权威的资料,所以敬请参考.如果你还没有学习NHibernate,请快速链接到NHibernate之旅系列文章导航. 实例分析 下面我用几个例子来分析

NHibernate之旅(6):探索NHibernate中的事务

本节内容 事务概述 1.新建对象 [测试成功提交] [测试失败回滚] 2.删除对象 3.更新对象 4.保存更新对象 结语 上一篇我们介绍了NHibernate中的Insert, Update, Delete操作,这篇我们来看看NHibernate中的事务.你通过它可以提交或者回滚你的操作. 事务概述 1.NHibernate中的事务(Transactions) 简单描述:要求ISession使用事务:做一些操作:提交或者回滚事务. 写成代码就像这样: ITransaction tx = _ses

[转]NHibernate之旅(6):探索NHibernate中的事务

本节内容 事务概述 1.新建对象 [测试成功提交] [测试失败回滚] 2.删除对象 3.更新对象 4.保存更新对象 结语 上一篇我们介绍了NHibernate中的Insert, Update, Delete操作,这篇我们来看看NHibernate中的事务.你通过它可以提交或者回滚你的操作. 事务概述 1.NHibernate中的事务(Transactions) 简单描述:要求ISession使用事务:做一些操作:提交或者回滚事务. 写成代码就像这样: ITransaction tx = _ses

NHibernate之旅系列文章导航

NHibernate之旅系列文章导航 宣传语 NHibernate.NHibernate教程.NHibernate入门.NHibernate下载.NHibernate教程中文版.NHibernate实例.NHibernate2.0.NHibernate2.0教程.NHibernate之旅.NHibernate工具 我的NHibernate全部文章在http://www.cnblogs.com/lyj/category/129155.html 导游 NHibernate是把Java的Hiberna

NHibernate之旅(4):探索查询之条件查询(Criteria Query)

本节内容 NHibernate中的查询方法 条件查询(Criteria Query) 1.创建ICriteria实例 2.结果集限制 3.结果集排序 4.一些说明 根据示例查询(Query By Example) 实例分析 结语 上一节,我们介绍了NHibernate查询语言的一种:NHibernate查询语言(HQL,NHibernate Query Language),这一节介绍一下条件查询(Criteria API). NHibernate中的查询方法 在NHibernate中提供了三种查

NHibernate之旅(1):开篇有益

NHibernate开篇有益 学习NHibernate有一段时间了,打算做个阶段性总结,就萌生了这个系列,这个系列参考NHibernate官方文档和Steve Bohlen的NHibernate之夏视频教程.作为开篇,首先了解多少人在使用NHibernate,先搞清楚NHibernate是什么?学习NHibernate的一些资源.也欢迎大家加入NHibernate中文社区. 这个系列我使用NHibernate官方2009年10月31日最新发布的NHibernate-2.1.1.GA版本.开发环境

[转]NHibernate之旅(1):开篇有益

本节内容 NHibernate是什么 NHibernate的架构 NHibernate资源 欢迎加入NHibernate中文社区 作者注:2009-11-06已更新 NHibernate开篇有益 学习NHibernate有一段时间了,打算做个阶段性总结,就萌生了这个系列,这个系列参考NHibernate官方文档和Steve Bohlen的NHibernate之夏视频教程.作为开篇,首先了解多少人在使用NHibernate,先搞清楚NHibernate是什么?学习NHibernate的一些资源.也