NHibernate系列文章十一:NHibernate并发控制
摘要
在同一时刻数据访问量和更新次数比较大的系统中,产生了数据的并发访问问题。并发访问使得在这样的环境中,所有用户(程序、实际用户、进程、线程等)的操作不产生负面问题。
如果不使用并发,在两个用户同时写同一条数据的时候,最后结果是不确定的。不使用并发的时候两个客户同时删除同一条数据,将产生异常终止程序。
并发控制方式有两种:悲观并发控制和乐观并发控制,NHibernate使用乐观并发控制。这篇文章首先介绍这两种策略,然后详细介绍NHibernate的乐观并发控制。
1. 悲观并发控制和乐观并发控制
悲观并发控制
悲观并发控制是基于控制锁的一种并发控制,在对一条数据的所有用户操作之前都加一把并发锁。在一个用户要对一条数据进行修改之前给这条记录加一把排他锁,其他用户要修改此记录必须等待,只有在前一个用户更新完该记录之后,或者主动释放锁,这个锁才解开,其他用户才能够重新得到这把锁,更新这条记录。
因为读者和写者阻塞,写者和写者阻塞,所以他可能产生数据争用冲突,而且效率一般比较低。
悲观并发控制主要用于数据争用比较激烈的环境,而且锁的成本低于数据回滚的成本的情况下。
乐观并发控制
乐观并发控制是基于版本号或者时间戳的一种并发控制。对所有需要进行并发控制的数据上加一个版本号或者时间戳的字段或者属性,当产生争用冲突的时候,检验数据版本是否最新。如果不是最新,则回滚当前用户对数据的写操作。
乐观并发控制主要用于数据争用不那么激烈的环境,偶尔数据争用时回滚事务的成本低于锁的成本的情况下。
2. NHibernate的乐观并发控制
NHibernate默认使用乐观并发控制,默认给所有NHibernate实体类对象添加了一个“程序看不见”的Version属性,通过持久化对象的版本进行并发控制。
但是我们可以通过给实体关系映射文件中添加并发控制字段(version或timestamp)来显示地观察NHibernate是怎样进行乐观并发控制的。
以version为例:
<version name="属性名" column="列名" type=".Net数据类型" unsaved-value="对象临时态时主键值"/>
- name为必填属性
- colonm名称如果和属性名相同时可以省略
- type类型是.Net基本类型时可以省略
- type类型是.Net基本数据类型时,NHibernate能够自动推断主键属性默认值,因此可以省略unsaved-value
timestamp是可以理解为不够强的版本控制,以对象的最后更新的时间戳为当前最新版本。
程序演示
给Customer表添加字段Version,类型为int,不为空,默认值为1。

数据库数据的初始值

修改Customer.hbm.xml文件,添加version节点
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernateDemoApp" namespace="NHibernateDemoApp">
<class name="Customer" table="Customer">
<id name="Id">
<generator class="native"/>
</id>
<version name="Version"/>
<property name="FirstName" not-null="true"/>
<property name="LastName" not-null ="true"/>
<property name="AverageRating"/>
<property name="Points"/>
<property name="HasGoldStatus"/>
<property name="MemberSince"/>
<property name="CreditRating" type="CustomerCreditRating"/>
<property name="Street"/>
<property name="City"/>
<property name="Province"/>
<property name="Country"/>
</class>
</hibernate-mapping>
修改Customer类,添加Version属性
public class Customer
{
public virtual int Id { get; set; }
public virtual int Version { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual double AverageRating { get; set; }
public virtual int Points { get; set; }
public virtual bool HasGoldStatus { get; set; }
public virtual DateTime MemberSince { get; set; }
public virtual CustomerCreditRating CreditRating { get; set; }
public virtual string Street { get; set; }
public virtual string City { get; set; }
public virtual string Province { get; set; }
public virtual string Country { get; set; }
}
1)修改操作的乐观并发控制
修改Main函数
static void Main(string[] args)
{
HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.Initialize(); using (var session = SessionFactory.OpenSession())
{
var customer1 = session.Get<Customer>();
var customer2 = session.Get<Customer>(); customer1.LastName = "Chen";
customer2.LastName = "Liu";
session.Update(customer1);
session.Flush();
session.Update(customer2);
session.Flush();
} Console.WriteLine("Completed");
Console.ReadLine();
}
打开NHibernate Profile,清空Session,执行程序,得到结果。

看到虽然调用了两次Session.Update和Session.Flush,但是实际上只执行了一次Update语句。只有最后一次Update被执行,第一次Update操作被自动回滚了。生成的Update语句中的查询条件增加了Version=1这个条件。
执行后数据库记录:

2)删除操作的乐观并发控制
修改Main函数
static void Main(string[] args)
{
HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.Initialize(); using (var session = SessionFactory.OpenSession())
{
var customer1 = session.Get<Customer>();
var customer2 = session.Get<Customer>(); session.Delete(customer1);
session.Delete(customer2);
session.Flush();
}
Console.WriteLine("Completed");
Console.ReadLine();
}
这里只用了一次Session.Flush,因为在第一次Flush后,持久化对象从持久化对象集合中删除了,此后调用Session.Delete方法会因为在持久化对象集合中找不到该对象而抛出异常。
清空NHibernate Profile的Session,执行程序,得到结果.

虽然调用了两次Session.Delete,但是只执行了一次Delete的SQL语句。
Id等于3的记录被删除了

NHibernate系列文章十一:NHibernate并发控制的更多相关文章
- NHibernate系列文章二十一:延迟加载
摘要 NHibernate的延迟加载机制是很重要的内容.通过关系映射将数据库表之间的关系映射成对象之间的关系,如果没有延迟加载机制,从主表的一个对象的查询将直接查询出所有与该对象关联的其他对象,如果关 ...
- NHibernate系列文章一:NHibernate介绍
摘要 NHibernate是一个成熟的开源的面向对象的.net映射框架.大量的实际项目中正在使用该框架.他是建立在ADO.Net基础之上.目前的版本是NHibernate 4.0.4.本系列文章都是基 ...
- NHibernate系列文章目录
第一章:NHibernate基础 NHibernate介绍 第一个NHibernate工程 简单的增删改查询 运行时监控 NHibernate配置 数据类型映射 Get/Load方法 NHiberna ...
- NHibernate系列文章二十七:NHibernate Mapping之Fluent Mapping基础(附程序下载)
摘要 从这一节起,介绍NHibernate Mapping的内容.前面文章都是使用的NHibernate XML Mapping.NHibernate XML Mapping是NHibernate最早 ...
- NHibernate系列文章二十三:NHibernate查询之Criteria查询(附程序下载)
摘要 上一篇文章介绍了NHibernate HQL,他的缺点是不能够在编译时发现问题.如果数据库表结构有改动引起了实体关系映射的类有改动,要同时修改这些HQL字符串.这篇文章介绍NHibernate面 ...
- NHibernate系列文章十五:NHibernate组件
摘要 前面文章介绍了NHibernate对简单.net数据类型的映射对照表.NHibernate也可以映射复杂数据类型,这里介绍通过组件映射NHibernate值对象. 1. NHibernate引用 ...
- NHibernate系列文章九:NHibernate对象二级缓存上
摘要 NHibernate的二级缓存由SessionFactory管理,由所有Session共享. NHibernate缓存读取顺序: 首先从一级缓存中读取,如果一级缓存对象存在,则读取一级缓存对象并 ...
- NHibernate系列文章十八:NHibernate关系之一对多(附程序下载)
摘要 这篇文章介绍NHibernate最实用的内容:关系映射. NHibernate的关系映射方式有三种: Set:无序对象集合,集合中每一个元素不能重复. List:有序对象集合,集合中的元素可以重 ...
- NHibernate系列文章十:NHibernate对象二级缓存下
摘要 上一节对NHibernate二级缓存做了简单介绍,NHibernate二级缓存是由SessionFactory管理的,所有Session共享.这一节介绍二级缓存其他两个方面:二级缓存查询和二级缓 ...
随机推荐
- wiglewifi
以上数据是使用wiglewifi软件收集的数据,把全部数据导出为KML格式,使用Google地球显示的,当然在大陆地区Google地球有些不正常,我使用的是lantern 以前使用lantern总是不 ...
- 【C++ STL编程】queue小例子
STL是标准化组件,现在已经是C++的一部分,因此不用额外安装什么. #include <queue> #include <iostream> using namespace ...
- SqlServer性能优化(一)
一:数据存储的方式: 1.数据文件:.mdf或.ndf 2.日志文件:.ldf 二:事务日志的工作步骤: 1.数据修改由应用程序发出(在缓冲区进行缓存) 2.数据页位于缓存区缓冲中,或者读入缓冲区缓存 ...
- NSFetchedResultsControllerDelegate不执行
熬了2 ,3个小时 才解决这个问题 在进行IM 设置时候 NSFetchRequest *request=[NSFetchRequest fetchRequestWithEntityName:@&q ...
- <转>九张图九句话
做个像石灰一样的人,别人越泼你冷水,你的人生越沸腾! 来自为知笔记(Wiz)
- Sublime Text 快捷键及插件安装
Sublime Text是一款跨平台的编辑器,它小巧绿色且速度非常快,支持各种流行编程语言的语法高亮.代码补全等,插件非常丰富!editplus.notepad++也都是不错的工具,体积轻巧,启动迅速 ...
- oracle之sqlplus讲解
这里要解释的sqlplus有2方面内容:sqlplus登陆命令和sql*plus工具命令. [sqlplus登陆命令] 常用的登陆命令有: sqlplus /nolog 登陆到sqlplus,还未登录 ...
- 关于nginx配置的不完全总结
请参考官方: http://wiki.nginx.org/HttpRewriteModule#Synopsis 关于缓存大小权限 error log 有如下日志:an upstream respons ...
- 【转】Android M(6.0) 权限爬坑之旅
原文网址:https://yanlu.me/android-m6-0-permission-chasm/ 有一篇全面介绍Android M 运行时权限文章写的非常全面:Android M 新的运行时权 ...
- sip_hangup_disposition
sip_hangup_disposition This variable contains the value of who sent the SIP BYE message. Some exampl ...