[NHibernate]并发控制

目录

写在前面

文档与系列文章

并发控制

乐观并发控制(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

 1     /// <summary>
 2     /// 描述:客户实体,数据库持久化类
 3     /// 创建人:wolfy
 4     /// 创建时间:2014-10-16
 5     /// </summary>
 6     public class Customer
 7     {
 8         /// <summary>
 9         /// 客户id
10         /// </summary>
11         public virtual Guid CustomerID { get; set; }
12         /// <summary>
13         /// 客户名字
14         /// </summary>
15         public virtual string CustomerName { get; set; }
16         /// <summary>
17         /// 版本控制
18         /// </summary>
19         public virtual int Version { get; set; }
20         /// <summary>
21         /// 客户地址
22         /// </summary>
23         public virtual string CustomerAddress { get; set; }
24     }

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

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <!--assembly:程序集,namespace:命名空间-->
 3 <hibernate-mapping  xmlns="urn:nhibernate-mapping-2.2" assembly="Wolfy.Shop.Domain"  namespace="Wolfy.Shop.Domain.Entities">
 4   <class name="Wolfy.Shop.Domain.Entities.Customer,Wolfy.Shop.Domain" table="TB_Customer">
 5     <!--主键-->
 6     <id name="CustomerID" type="Guid" unsaved-value="null">
 7
 8       <column name="CustomerID" sql-type="uniqueidentifier" not-null="true" unique="true"/>
 9       <generator class="assigned"></generator>
10     </id>
11     <!--版本控制-->
12     <version name="Version" column="Version" type="integer"  unsaved-value="0"/>
13     <property name="CustomerName" type="String">
14       <column name="CustomerName" sql-type="nvarchar"  not-null="false"/>
15     </property>
16     <property name="CustomerAddress" type="String">
17       <column name="CustomerAddress" sql-type="nvarchar"  not-null="false"/>
18     </property>
19   </class>
20 </hibernate-mapping>

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

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

并发更新控制

数据库中的数据有

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

数据层代码

 1         /// <summary>
 2         /// 通过事务的方式添加或者修改
 3         /// </summary>
 4         /// <param name="customer">添加的对象</param>
 5         /// <returns>是否成功</returns>
 6         public bool SaveOrUpdateByTrans(Customer customer)
 7         {
 8             NHibernateHelper nhibernateHelper = new NHibernateHelper();
 9             var session = nhibernateHelper.GetSession();
10             using (ITransaction transaction = session.BeginTransaction())
11             {
12                 try
13                 {
14                     session.SaveOrUpdate(customer);
15                     session.Flush();
16                     //成功则提交
17                     transaction.Commit();
18                     return true;
19                 }
20                 catch (Exception)
21                 {
22                     //出现异常,则回滚
23                     transaction.Rollback();
24                     return false;
25                 }
26             }
27         }

测试数据

 1         /// <summary>
 2         /// 并发更新操作
 3         /// </summary>
 4         /// <param name="sender"></param>
 5         /// <param name="e"></param>
 6         protected void btnSameTimeUpdate_Click(object sender, EventArgs e)
 7         {
 8             Guid guidCustomerId = new Guid("82724514-682E-4E6F-B759-02E499CDA50F");
 9             //模拟第一个修改数据
10             Customer c1 = new Customer()
11             {
12                 CustomerID = guidCustomerId,
13                 CustomerAddress = "南京",
14                 CustomerName = "wanger",
15                 Version = 1
16             };
17             //模拟第二个修改数据
18             Customer c2 = new Customer()
19             {
20                 CustomerID = guidCustomerId,
21                 CustomerAddress = "黑龙江",
22                 CustomerName = "wanger",
23                 Version = 1
24             };
25
26             Business.CustomerBusiness customerBusiness = new Business.CustomerBusiness();
27             customerBusiness.SaveOrUpdateByTrans(c1);
28             customerBusiness.SaveOrUpdateByTrans(c2);
29         }

修改后的数据

同对比上面的数据库中的数据,我们发现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

时间: 2024-11-08 21:35:48

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

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

Nhibernate 并发控制 [1]悲观并发控制 正在使用数据的操作,加上锁,使用完后解锁释放资源. 使用场景:数据竞争激烈,锁的成本低于回滚事务的成本 缺点:阻塞,可能死锁 [2]乐观并发控制: 所谓乐观,就是乐观的认为其他人没有在用该资源,资源的使用者不加锁. A 读取数据后,如果该数据被别人B修改,产生错误,A回滚事务并重新开始. 使用场景:数据竞争不激烈,回滚事务的成本低于锁的成本. ---------------------------------------------------

[NHibernate]存储过程的使用(三)

目录 写在前面 文档与系列文章 查询 总结 写在前面 前面的文章介绍了在nhibernate中使用存储过程进行增删改的操作,当然查询也是可以的,在nhibernate中也可以执行任意的存储过程.本篇文章将介绍如何使用查询的存储过程的方式. 文档与系列文章 [Nhibernate]体系结构 [NHibernate]ISessionFactory配置 [NHibernate]持久化类(Persistent Classes) [NHibernate]O/R Mapping基础 [NHibernate]

[NHibernate]立即加载

目录 写在前面 文档与系列文章 立即加载 一个例子 总结 写在前面 上篇文章介绍了nhibernate延迟加载的相关内容,简单回顾一下延迟加载,就是需要的时候再去加载,需要的时候再向数据库发出sql指令进行查询. 本篇文章介绍的立即加载,则和延迟加载相对,举个简单的例子,就是查询客户信息,则会将该客户相关联的数据立即进行加载.实现立即加载的方式有三种:设置映射文件中节点的可选属性lazy,Nhibernate提供的实用类,HQL抓取策略. 文档与系列文章 [Nhibernate]体系结构 [NH

[NHibernate]延迟加载

目录 写在前面 文档与系列文章 延迟加载 一个例子 总结 写在前面 上篇文章介绍了多对多关系的关联查询的sql,HQL,Criteria查询的三种方式.本篇文章将介绍nhibernate中的延迟加载方式,延迟加载按个人理解也可以叫做按需要加载(Loading-on-demand). 文档与系列文章 [Nhibernate]体系结构 [NHibernate]ISessionFactory配置 [NHibernate]持久化类(Persistent Classes) [NHibernate]O/R

耗时两月,NHibernate系列出炉

写在前面 这篇总结本来是昨天要写的,可昨天大学班长来视察工作,多喝了点,回来就倒头就睡了,也就把这篇总结的文章拖到了今天. nhibernate系列从开始着手写,到现在前后耗费大概两个月的时间,通过总结这个系列,确实收获不小,这里将本系列的导航,列出放在这里算是对nhibernate系列的一个小总结,也方便想学习nhibernate的朋友查找. nhibernate文档 文档部分是从网上搜集,认为还是比较全面的,就在博客上做了记录,也希望在用到的时候,有个地方可以方便的查找. [Nhiberna

[NHibernate]代码生成器的使用

目录 写在前面 文档与系列文章 代码生成器的使用 总结 写在前面 前面的文章介绍了nhibernate的相关知识,都是自己手敲的代码,有时候显得特别的麻烦,比如你必须编写持久化类,映射文件等等,举得例子比较简单,字段比较少,如果一个数据表有几十个字段,你手敲的话烦都烦死了,不过为了学习nhibernate的语法,初学的过程还是建议自己动手,这样敲的多了就对每个过程,及映射文件中的节点的含义有个大致印象.前面举的例子,你也会发现这种方式的步骤就是首先创建数据库,然后编写持久化类,映射文件,最后编写

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

目录 写在前面 文档与系列文章 多对多关系关联查询 总结 写在前面 上篇文章介绍了nhibernate中对一对多关系进行关联查询的几种方式,以及在使用过程需要注意的问题.这篇文章对多对多关系的查询处理也采用上篇文章的描述方式进行说明. 文档与系列文章 [Nhibernate]体系结构 [NHibernate]ISessionFactory配置 [NHibernate]持久化类(Persistent Classes) [NHibernate]O/R Mapping基础 [NHibernate]集合

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

目录 写在前面 文档与系列文章 一对多查询 总结 写在前面 上篇文章介绍了nhibernate的一对多关系如何配置,以及级联删除,级联添加数据的内容.这篇文章我们将学习nhibernate中的一对多关系的关联查询.前面文章中也介绍的nhibernate的查询:HQL,条件查询,原生SQL查询. 文档与系列文章 [Nhibernate]体系结构 [NHibernate]ISessionFactory配置 [NHibernate]持久化类(Persistent Classes) [NHibernat

[NHibernate]存储过程的使用(一)

目录 写在前面 文档与系列文章 Nhibernate中使用存储过程 一个例子 总结 写在前面 上篇文章一个小插曲,分析了延迟加载是如何解决N+1 select查询问题的.这篇开始介绍在nhibernate中如何使用存储过程,同时也介绍如何使用代码生成器,提高开发效率. 文档与系列文章 [Nhibernate]体系结构 [NHibernate]ISessionFactory配置 [NHibernate]持久化类(Persistent Classes) [NHibernate]O/R Mapping