摘要

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

如果不使用并发,在两个用户同时写同一条数据的时候,最后结果是不确定的。不使用并发的时候两个客户同时删除同一条数据,将产生异常终止程序。

并发控制方式有两种:悲观并发控制和乐观并发控制,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并发控制的更多相关文章

  1. NHibernate系列文章二十一:延迟加载

    摘要 NHibernate的延迟加载机制是很重要的内容.通过关系映射将数据库表之间的关系映射成对象之间的关系,如果没有延迟加载机制,从主表的一个对象的查询将直接查询出所有与该对象关联的其他对象,如果关 ...

  2. NHibernate系列文章一:NHibernate介绍

    摘要 NHibernate是一个成熟的开源的面向对象的.net映射框架.大量的实际项目中正在使用该框架.他是建立在ADO.Net基础之上.目前的版本是NHibernate 4.0.4.本系列文章都是基 ...

  3. NHibernate系列文章目录

    第一章:NHibernate基础 NHibernate介绍 第一个NHibernate工程 简单的增删改查询 运行时监控 NHibernate配置 数据类型映射 Get/Load方法 NHiberna ...

  4. NHibernate系列文章二十七:NHibernate Mapping之Fluent Mapping基础(附程序下载)

    摘要 从这一节起,介绍NHibernate Mapping的内容.前面文章都是使用的NHibernate XML Mapping.NHibernate XML Mapping是NHibernate最早 ...

  5. NHibernate系列文章二十三:NHibernate查询之Criteria查询(附程序下载)

    摘要 上一篇文章介绍了NHibernate HQL,他的缺点是不能够在编译时发现问题.如果数据库表结构有改动引起了实体关系映射的类有改动,要同时修改这些HQL字符串.这篇文章介绍NHibernate面 ...

  6. NHibernate系列文章十五:NHibernate组件

    摘要 前面文章介绍了NHibernate对简单.net数据类型的映射对照表.NHibernate也可以映射复杂数据类型,这里介绍通过组件映射NHibernate值对象. 1. NHibernate引用 ...

  7. NHibernate系列文章九:NHibernate对象二级缓存上

    摘要 NHibernate的二级缓存由SessionFactory管理,由所有Session共享. NHibernate缓存读取顺序: 首先从一级缓存中读取,如果一级缓存对象存在,则读取一级缓存对象并 ...

  8. NHibernate系列文章十八:NHibernate关系之一对多(附程序下载)

    摘要 这篇文章介绍NHibernate最实用的内容:关系映射. NHibernate的关系映射方式有三种: Set:无序对象集合,集合中每一个元素不能重复. List:有序对象集合,集合中的元素可以重 ...

  9. NHibernate系列文章十:NHibernate对象二级缓存下

    摘要 上一节对NHibernate二级缓存做了简单介绍,NHibernate二级缓存是由SessionFactory管理的,所有Session共享.这一节介绍二级缓存其他两个方面:二级缓存查询和二级缓 ...

随机推荐

  1. <转>九张图九句话

    ​ 做个像石灰一样的人,别人越泼你冷水,你的人生越沸腾! 来自为知笔记(Wiz)

  2. Xcode中使用debug模式和release模式

    在开发过程中,我们经常需要用到NSLog输出一些信息,甚至有的开发过程,必须在控制台查看输出,有经验的程序员通过控制台输出就能知道整个数据交互的一个流程.但是一个发布的程序,里面带有太多的NSLog输 ...

  3. xmind的第七天笔记

  4. Android studio工具介绍

    区域1介绍 这个区域是运行和调试相关的操作. 1.编译2中显示的模块                      2.当前项目的模块列表            3.运行2中显示的模块           ...

  5. NSString字符串

    要把 “2011-11-29” 改写成 “2011/11/29”一开始想用ios的时间格式,后来用NSString的方法搞定. [string stringByReplacingOccurrences ...

  6. mac 下更新 .bash_profile 文件

    1.打开terminal(终端) 2.cd ~ ( 进入当前用户的home目录) 3.open .bash_profile (打开.bash_profile文件,如果文件不存在就  创建文件:touc ...

  7. Unity3D研究院之获取摄像机的视口区域

    摄像机分为两种,一种是正交摄像机还有一种是透视摄像机.正交摄像机无论远近它的视口范围永远是固定的,但是透视摄像机是由原点向外扩散性发射,也就是距离越远它的视口区域也就越大.那么我们如何获取距离摄像机任 ...

  8. Android学习七:new Date使用

    1.例子 学习时间函数,并实现了简单的多个按钮监听同一个事件的方法 2.代码 代码很简单,也很清晰 package com.example.datetime; import java.text.Sim ...

  9. mysql操作汇集

    1.修改root密码 cmd进如mysql的bin目录 >mysql -u root -p Enter password: ****** mysql> use mysql; mysql&g ...

  10. linux服务之tuned

    RHEL/CentOS 在 6.3 版本以后引入了一套新的系统调优工具 tuned/tuned-adm,其中 tuned 是服务端程序,用来监控和收集系统各个组件的数据,并依据数据提供的信息动态调整系 ...