目录

写在前面

文档与系列文章

并发控制

乐观并发控制(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. markdown简要说明源码

    ##markdown ###什么是markdown:    Markdown是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式.  Markdown具有 ...

  2. WinCE项目应用汇总

    虚拟仪器(VI) RM905a+医用放射性核素活度计 RM905a+活度计远程检定方法研究 车载导航

  3. 洛谷P1330封锁阳光大学[二分图染色]

    题目描述 曹是一只爱刷街的老曹,暑假期间,他每天都欢快地在阳光大学的校园里刷街.河蟹看到欢快的曹,感到不爽.河蟹决定封锁阳光大学,不让曹刷街. 阳光大学的校园是一张由N个点构成的无向图,N个点之间由M ...

  4. Java程序设计之线程池应用

    这里简单说一个线程池的小应用,从控制台输入线程访问的线程数量,输出输出工作的线程名,之前先构造一个包含了5条线程的对象. 代码: import java.util.Scanner; import ja ...

  5. MAVEN构建Spring +Spring mvc + Mybatis 项目(Maven配置部分(workshop))

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...

  6. jquery的show/hide/toggle详解

    通过阅读源码我们发现show,hide,toggle调用了showHide和isHidden这2个方法,所以我们要搞明白原理必须先看一下这2个方法. jQuery.fn.extend({ ...... ...

  7. mysql重置密码

    1.首先停止正在运行的MySQL进程 复制代码代码如下: >net stop mysql  如未加载为服务,可直接在进程管理器或者服务中进行关闭. 2.以安全模式启动MySQL 进入mysql目 ...

  8. C#进阶系列——DDD领域驱动设计初探(一):聚合

    前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下DDD这么一个听上去高大上的 ...

  9. 关于Interception框架

    对于OOP来说,是个树形结构,如果要实现多个子数之间的共享服务,例如很多子树都用到日志服务,这时候AOP的横切关注 cross cutting concerns就非常有用了.如果要使每个类具备一项功能 ...

  10. 延迟容忍网络(Delay-tolerant networking)

    标签: 网络networking存储工作network路由器 2012-03-24 10:01 3702人阅读 评论(0) 收藏 举报 分类: 计算机网络(12) 版权声明:本文为博主原创文章,对文章 ...