目录

写在前面

文档与系列文章

并发控制

乐观并发控制(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. nodejs缓冲模块buffer相关资料

    buffer模块的详细使用教程 浅析nodejs的buffer类 深入浅出NodeJS--Buffer Node Buffer/Stream 内存策略分析

  2. java弱引用之WeakHashMap相关资料

    本人博客中有一篇文章对java中的引用有详细的介绍[http://www.cnblogs.com/javaee6/p/4763190.html],java中WeakHashMap这个类就是java弱引 ...

  3. 移动端报表JS开发示例--获取定位

    上次分享了移动端报表JS开发的系统概念,后来我又回去摸索了一些案例.之前接触到的FineReport的APP客户端可以用来打卡签到,就好奇研究了以下,这次就来聊一聊报表移动端开发如何实现定位功能. 1 ...

  4. Label控件如何根据字符串自定义大小

    一.. this.label_Msg.AutoSize = false;  //设置label空件不能自动大小 二.. 用代码控制label控件的大小 1.根据字符串.label的宽度 计算字符串的面 ...

  5. hdu 4704 Sum (整数和分解+快速幂+费马小定理降幂)

    题意: 给n(1<n<),求(s1+s2+s3+...+sn)mod(1e9+7).其中si表示n由i个数相加而成的种数,如n=4,则s1=1,s2=3.                  ...

  6. NYOJ 187

    快速查找素数 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描述 现在给你一个正整数N,要你快速的找出在2.....N这些数里面所有的素数. 输入 给出一个正整数数N(N&l ...

  7. 用于部署war并重启Tomcat的脚本

    只需要定义两个变量, 一个是目标tomcat实例的目录, 另一个是war包的名称 # Please define the absolute path of tomcat instance THIS_T ...

  8. 国内外前端(js)开发框架对比

    国内外前端开发框架对比 首先我们先对目前国内外主流前端开发框架做一个基本的了解,之后再对他们进行一个直观的对比. Bootstrap Bootstrap(http://www.bootcss.com) ...

  9. ReactNative新手学习之路01-创建项目开始

    新手学习之路01-创建项目开始 小菜鸟准备学习RN开发,决定写下自己的学习历程,方便其他也想要学习RN的人,后期会持续更新写下自己所有学习经历,一步步从菜鸟成长成业内高手.开发环境准备,本文默认环境已 ...

  10. C#基础系列——异步编程初探:async和await

    前言:前面有篇从应用层面上面介绍了下多线程的几种用法,有博友就说到了async, await等新语法.确实,没有异步的多线程是单调的.乏味的,async和await是出现在C#5.0之后,它的出现给了 ...