目录

写在前面

文档与系列文章

并发控制

乐观并发控制(Optimistic Concurrency)

一个例子

悲观并发控制(Pessimistic Concurrency)

总结

写在前面

上篇文章介绍了nhibernate中的事务,在增删改查中使用的必要性。本篇文章将介绍nhibernate中的并发控制。对多人同时修改同一条数据,如何进行并发控制,在nhibernate中提供了一些方法来实现乐观并发控制。

文档与系列文章

[Nhibernate]体系结构

[NHibernate]ISessionFactory配置

[NHibernate]持久化类(Persistent Classes)

[NHibernate]O/R Mapping基础

[NHibernate]集合类(Collections)映射 

[NHibernate]关联映射

[NHibernate]Parent/Child

[NHibernate]缓存(NHibernate.Caches)

[NHibernate]NHibernate.Tool.hbm2net

[NHibernate]Nullables

[NHibernate]Nhibernate如何映射sqlserver中image字段

[NHibernate]基本配置与测试 

[NHibernate]HQL查询 

[NHibernate]条件查询Criteria Query

[NHibernate]增删改操作

[NHibernate]事务

并发控制

什么是并发控制?

当很多人试图同时修改数据库中的数据时,必须有这样一种控制,使一个人的操作不对他人的操作产生负面影响,这就是并发控制。

说的更简单点就是,2个或者多个用户(实际用户,服务,多线程)同时编辑相同数据时,及其在连接或者断开情况下可能发生的情况。

并发控制理论根据控制方法而分为两类:乐观并发控制和悲观并发控制。

乐观并发控制(Optimistic Concurrency)

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

NHibernate提供了一些方法实现乐观并发控制,在配置文件中:定义了<version>和<timespan>节点,其中<version>节点用于版本控制,表明数据表中数据的版本信息。<timespan>用于时间戳跟踪,表明数据表中包含时间戳数据。时间戳本质上是一种乐观锁定不太安全的实现。通常而言,版本控制方式是首选的方式。

看一下映射文件中version和timestamp节点的属性:

属性说明:

access(默认为property):Nhibernate用于访问特性值的策略。

column(默认为特性名):指定具有版本号或者时间戳的字段名。

gennerated:生成属性,可选never和always两个值。

name:持久化类的特性名或者指定类型为.NET类型DateTime的特性名。

type(默认Int32):版本号的类型,可选类型为Int64、Int32、Int16、Ticks、Timestamp、TimeSpan。注意:<timestamp>和<version type="timestamp">是等价的。

unsaved-value(在版本控制中默认是“敏感”值,在时间截默认是null):表示某个实例刚刚被实例化(尚未保存)时的版本特性值,依靠这个值就可以把这种情况和已经在先前的会话中保存或装载的游离实例区分开来。(undefined指明使用标识特性值进行判断).

一个例子

使用版本控制方式进行乐观并发控制,修改持久化类Customer,添加版本控制属性Version

     /// <summary>
/// 描述:客户实体,数据库持久化类
/// 创建人:wolfy
/// 创建时间:2014-10-16
/// </summary>
public class Customer
{
/// <summary>
/// 客户id
/// </summary>
public virtual Guid CustomerID { get; set; }
/// <summary>
/// 客户名字
/// </summary>
public virtual string CustomerName { get; set; }
/// <summary>
/// 版本控制
/// </summary>
public virtual int Version { get; set; }
/// <summary>
/// 客户地址
/// </summary>
public virtual string CustomerAddress { get; set; }
}

修改映射文件Customer.hbm.xml,添加version映射节点。

 <?xml version="1.0" encoding="utf-8" ?>
<!--assembly:程序集,namespace:命名空间-->
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Wolfy.Shop.Domain" namespace="Wolfy.Shop.Domain.Entities">
<class name="Wolfy.Shop.Domain.Entities.Customer,Wolfy.Shop.Domain" table="TB_Customer">
<!--主键-->
<id name="CustomerID" type="Guid" unsaved-value="null"> <column name="CustomerID" sql-type="uniqueidentifier" not-null="true" unique="true"/>
<generator class="assigned"></generator>
</id>
<!--版本控制-->
<version name="Version" column="Version" type="integer" unsaved-value="0"/>
<property name="CustomerName" type="String">
<column name="CustomerName" sql-type="nvarchar" not-null="false"/>
</property>
<property name="CustomerAddress" type="String">
<column name="CustomerAddress" sql-type="nvarchar" not-null="false"/>
</property>
</class>
</hibernate-mapping>

修改数据表TB_Customer,添加version字段,默认值为1。

alter table tb_customer add [Version] int not null default 1

并发更新控制

数据库中的数据有

测试同时修改客户id为“82724514-682E-4E6F-B759-02E499CDA50F”名字为“wanger”的客户信息,两个操作同时修改客户住址为“南京”和“黑龙江”。代码如下:

数据层代码

         /// <summary>
/// 通过事务的方式添加或者修改
/// </summary>
/// <param name="customer">添加的对象</param>
/// <returns>是否成功</returns>
public bool SaveOrUpdateByTrans(Customer customer)
{
NHibernateHelper nhibernateHelper = new NHibernateHelper();
var session = nhibernateHelper.GetSession();
using (ITransaction transaction = session.BeginTransaction())
{
try
{
session.SaveOrUpdate(customer);
session.Flush();
//成功则提交
transaction.Commit();
return true;
}
catch (Exception)
{
//出现异常,则回滚
transaction.Rollback();
return false;
}
}
}

测试数据

         /// <summary>
/// 并发更新操作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnSameTimeUpdate_Click(object sender, EventArgs e)
{
Guid guidCustomerId = new Guid("82724514-682E-4E6F-B759-02E499CDA50F");
//模拟第一个修改数据
Customer c1 = new Customer()
{
CustomerID = guidCustomerId,
CustomerAddress = "南京",
CustomerName = "wanger",
Version =
};
//模拟第二个修改数据
Customer c2 = new Customer()
{
CustomerID = guidCustomerId,
CustomerAddress = "黑龙江",
CustomerName = "wanger",
Version =
}; Business.CustomerBusiness customerBusiness = new Business.CustomerBusiness();
customerBusiness.SaveOrUpdateByTrans(c1);
customerBusiness.SaveOrUpdateByTrans(c2);
}

修改后的数据

同对比上面的数据库中的数据,我们发现Version版本变成2了,而且修改的客户地址为“黑龙江”信息并没有修改成功。监控的sql发现,两次update确实都提交了,但是只有第一次修改了。如图:

细心的你可能会发现@p0的值在改变,并且在修改的时候多了一个Where子句

WHERE CustomerID = @p3 AND Version = @p4'

这就是为什么第二次为什么没有修改成功了,因为已经找不到这条数据了。

悲观并发控制(Pessimistic Concurrency)

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

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

总结

本文讲述nhibernate中乐观并发控制的版本控制方式,列举了一个并发修改的例子(很可能你会说那不是顺序执行的吗?你也看到这种顺序执行,也无法修改),在分析生成的sql语句中,你会发现如果加上版本控制,在修改的时候会在where子句中加上版本号。

参考文章:http://www.cnblogs.com/lyj/archive/2008/10/21/1316269.html

[NHibernate]并发控制的更多相关文章

  1. NHibernate系列文章十一:NHibernate并发控制

    摘要 在同一时刻数据访问量和更新次数比较大的系统中,产生了数据的并发访问问题.并发访问使得在这样的环境中,所有用户(程序.实际用户.进程.线程等)的操作不产生负面问题. 如果不使用并发,在两个用户同时 ...

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

    Nhibernate 并发控制 [1]悲观并发控制 正在使用数据的操作,加上锁,使用完后解锁释放资源. 使用场景:数据竞争激烈,锁的成本低于回滚事务的成本 缺点:阻塞,可能死锁 [2]乐观并发控制: ...

  3. 耗时两月,NHibernate系列出炉

    写在前面 这篇总结本来是昨天要写的,可昨天大学班长来视察工作,多喝了点,回来就倒头就睡了,也就把这篇总结的文章拖到了今天. nhibernate系列从开始着手写,到现在前后耗费大概两个月的时间,通过总 ...

  4. [NHibernate]组件之依赖对象

    目录 写在前面 文档与系列文章 组件之依赖对象 一个例子 总结 写在前面 周一至周四一直在成都出差,也一直没有更新博客了,一回到家第一件事就是扒一扒最近博客园更新的文章,然后把想看的收藏了,大概有20 ...

  5. [NHibernate]一对多关系(级联删除,级联添加)

    目录 写在前面 文档与系列文章 一对多关系 一个例子 级联删除 级联保存 总结 写在前面 在前面的文章中,我们只使用了一个Customer类进行举例,而在客户.订单.产品中它们的关系,咱们并没有涉及, ...

  6. [NHibernate]一对多关系(关联查询)

    目录 写在前面 文档与系列文章 一对多查询 总结 写在前面 上篇文章介绍了nhibernate的一对多关系如何配置,以及级联删除,级联添加数据的内容.这篇文章我们将学习nhibernate中的一对多关 ...

  7. [NHibernate]多对多关系(关联查询)

    目录 写在前面 文档与系列文章 多对多关系关联查询 总结 写在前面 上篇文章介绍了nhibernate中对一对多关系进行关联查询的几种方式,以及在使用过程需要注意的问题.这篇文章对多对多关系的查询处理 ...

  8. [NHibernate]延迟加载

    目录 写在前面 文档与系列文章 延迟加载 一个例子 总结 写在前面 上篇文章介绍了多对多关系的关联查询的sql,HQL,Criteria查询的三种方式.本篇文章将介绍nhibernate中的延迟加载方 ...

  9. [NHibernate]立即加载

    目录 写在前面 文档与系列文章 立即加载 一个例子 总结 写在前面 上篇文章介绍了nhibernate延迟加载的相关内容,简单回顾一下延迟加载,就是需要的时候再去加载,需要的时候再向数据库发出sql指 ...

随机推荐

  1. jsp/servlet 中sendRedirect,include,forward区别

    1 sendRedirect response.sendRedirect(); 服务器根据逻辑,发送一个状态码,告诉浏览器重新去请求新的地址,一般来说浏览器会用刚才请求的所有参数重新请求,所以sess ...

  2. Nginx负载均衡配置说明

    WEB服务做负载均衡的方法有很多种,但使用Nginx做负载均衡部署毫无疑问是非常高效也是非常流行的一种. 本人大多数做.NET开发,但部署负载却一直用Nginx,对其他的负载方式研究不多,只测试过一次 ...

  3. 【转载、推荐】不要自称是程序员,我十多年的 IT 职场总结

    注评:一气读完后,有些和我的观点类似.这篇文章显然是外国老写的,但是不妨碍我们的跨国交流. 如果我可以给每个工程教育增加一门课,它不会涉及编译器.门电路或是时间复杂度,而是一门介绍行业现实的入门课,因 ...

  4. Caffe源码解析5:Conv_Layer

    转载请注明出处,楼燚(yì)航的blog,http://home.cnblogs.com/louyihang-loves-baiyan/ Vision_layer里面主要是包括了一些关于一些视觉上的操 ...

  5. Android开发资源汇总

    搜索 虫部落快搜(已被墙) Google免FQ镜像汇总 Lantern stackoverflow 博客 Android基础&进阶 深入理解Android 老罗的Android之旅 Andro ...

  6. http status 状态码汇总

    常见HTTP状态码 200 OK 301 Moved Permanently 302 Found 304 Not Modified 307 Temporary Redirect 400 Bad Req ...

  7. 洛谷练习P2279 P1346

    题目描述 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地.起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两个基地都能够通过道路到达,所以所有的基地形成了一个巨大的树状 ...

  8. 第18章 集合框架(2)-Set接口

    第18章 集合框架(2)-Set接口 Set是Collection子接口,模拟了数学上的集的概念 Set集合存储特点 1.不允许元素重复 2.不会记录元素的先后添加顺序 Set只包含从Collecti ...

  9. pycharm的使用破解和Anaconda的使用

    1.pycharm的破解: 版本: pycharm 2016.2.3 链接: 下载专业版本   下面是这个版本的注册码: 43B4A73YYJ-eyJsaWNlbnNlSWQiOiI0M0I0QTcz ...

  10. [No00007E]2016-面经[中]

    目录: 写一份动人简历的九个步奏 英文简历必备的十大元素 写一份动人简历的九个步骤 写一份动人的简历可以算得上是找工作最难的部分之一,但是,通过下面九步,这件事不再那么难了. 简历定位.雇主们之所以花 ...