本节内容

  • 什么是并发控制?

    • 悲观并发控制(Pessimistic Concurrency)
    • 乐观并发控制(Optimistic Concurrency)
  • NHibernate支持乐观并发控制
  • 实例分析
  • 结语

什么是并发控制?

当很多人试图同一时候改动数据库中的数据时,必须实现一个控制系统,使一个人所做的改动不会对他人所做的改动产生负面影响。

这称为并发控制。

简单的理解就是2个或多个用者同一时候编辑同样的数据。

这里的用者可能是:实际用户、不同服务、不同的代码段(使用多线程),及其在断开式和连接式情况下可能发生的情况。

并发控制理论依据建立并发控制的方法而分为两类:

悲观并发控制(Pessimistic Concurrency)

一个锁定系统,能够阻止用户以影响其它用户的方式改动数据。假设用户运行的操作导致应用了某个锁,仅仅有这个锁的全部者释放该锁。其它用户才干运行与该锁冲突的操作。

这样的方法之所以称为悲观并发控制,是由于它主要用于数据争用激烈的环境中。以及发生并发冲突时用锁保护数据的成本低于回滚事务的成本的环境中。

简单的理解通常通过“独占锁”的方法。获取锁来堵塞对于别的进程正在使用的数据的訪问。换句话说,读者和写者之间是会互相堵塞的 ,这可能导致数据同步冲突。

乐观并发控制(Optimistic Concurrency)

在乐观并发控制中。用户读取数据时不锁定数据。当一个用户更新数据时。系统将进行检查。查看该用户读取数据后其它用户是否又更改了该数据。假设其它用户更新了数据。将产生一个错误。普通情况下,收到错误信息的用户将回滚事务并又一次開始。这样的方法之所以称为乐观并发控制。是因为它主要在下面环境中使用:数据争用不大且偶尔回滚事务的成本低于读取数据时锁定数据的成本。

(以上摘自SQL Server2008 MSDN文档)

NHibernate支持乐观并发控制

NHibernate提供了一些方法来支持乐观并发控制:在映射文件里定义了<version> 节点和<timestamp>节点。当中<version> 节点用于版本号控制,表明表中包括附带版本号信息的数据。

<timestamp>节点用于时间截跟踪。表明表中包括时间戳数据。

时间戳本质上是一种对乐观锁定不是特别安全的实现。可是通常而言,版本号控制方式是首选的方法。当然。有时候应用程序可能在其它方面使用时间戳。

以下用两幅图显示这两个节点映射属性:

看看它们的意义:

  • access(默觉得property):NHibernate用于訪问特性值的策略。
  • column(默觉得特性名):指定持有版本号号的字段名或者持有时间戳的字段名。

  • generated:生成属性,可选never和always两个属性。
  • name:持久化类的特性名或者指定类型为.NET类型DateTime的特性名。
  • type(默觉得Int32):版本号号的类型,可选类型为Int64、Int32、Int16、Ticks、Timestamp、TimeSpan。注意:<timestamp>和<version type="timestamp">是等价的。
  • unsaved-value(在版本号控制中默认是“敏感”值,在时间截默认是null):表示某个实例刚刚被实例化(尚未保存)时的版本号特性值,依靠这个值就能够把这样的情况和已经在先前的会话中保存或装载的游离实例区分开来。(undefined指明使用标识特性值进行推断)

实例分析

以下用一个样例来实现乐观并发控制,这里使用Version版本号控制。

1.改动持久化Customer类:加入Version属性

public class Customer
{
public virtual int CustomerId { get; set; }
//版本号控制
public virtual int Version { get; set; }
public virtual string Firstname { get; set; }
public virtual string Lastname { get; set; }
}

2.改动映射文件:加入Version映射节点

<?xml version="1.0" encoding="utf-8" ?

>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="DomainModel" namespace="DomainModel">
<class name ="DomainModel.Entities.Customer,DomainModel" table="Customer">
<id name="CustomerId" column="CustomerId" type="Int32" unsaved-value="0">
<generator class ="native"></generator>
</id>
<version name="Version" column="Version" type="integer" unsaved-value="0"/>
<property name="Firstname" column ="Firstname" type="string" length="50" not-null="false"/>
<property name ="Lastname" column="Lastname" type="string" length="50" not-null="false"/>
</class>
</hibernate-mapping>

3.改动数据库,加入Version字段

详细參数:[Version] [int] NOT NULL 默认值为1,当然了改动数据库是最原始的方式了,假设你会使用SchemaExport,能够直接利用持久化类和映射文件生成数据库。以后在介绍怎样使用这个。

4.并发更新測试

在測试之前,我们先看看数据库中什么数据,预知一下:

编写并发更新測试代码:

查询2次CustomerId为1的客户,这里就是上面的第一条数据,第一个改动为"CnBlogs",第二个改动为"www.cnblogs.com",两者同一时候更新提交。你想想发生什么情况?

[Test]
public void UpdateConcurrencyViolationCanotThrowException()
{
Customer c1 = _transaction.GetCustomerById(1);
Customer c2 = _transaction.GetCustomerById(1);
c1.Name.Firstname = "CnBlogs";
c2.Name.Firstname = "www.cnblogs.com"; _transaction.UpdateCustomerTransaction(c1);
_transaction.UpdateCustomerTransaction(c2);
}

让我们去看看数据库吧。一目了然:

我们发现CustomerId为1的客户更新了FirstName数据,而且Version更新为2。

你知道什么原理了吗?看看这步NHibernate生成的SQL语句(我的可能比你的不一样):先查询数据库,在直接更新数据,看看NHibernate多么实在,明显做了一些优化工作。

SELECT customer0_.CustomerId as CustomerId3_0_,
customer0_.Version as Version3_0_,
customer0_.Firstname as Firstname3_0_,
customer0_.Lastname as Lastname3_0_,
customer0_1_.OrderDiscountRate as OrderDis2_4_0_,
customer0_1_.CustomerSince as Customer3_4_0_,
case
when customer0_1_.CustomerId is not null then 1
when customer0_.CustomerId is not null then 0
end
as clazz_0_ FROM Customer
customer0_ left outer join PreferredCustomer customer0_1_
on customer0_.CustomerId=customer0_1_.CustomerId
WHERE customer0_.CustomerId=@p0; @p0 = '1' UPDATE Customer SET Version = @p0, Firstname = @p1, Lastname = @p2
WHERE CustomerId = @p3 AND Version = @p4;
@p0 = '2', @p1 = 'www.cnblogs.com', @p2 = 'Lee', @p3 = '1', @p4 = '1'

5.并发删除測试

我们再来编写一个測试用于并发删除。

查询2次CustomerId为2的客户。这里就是上面的第二条数据,两者同一时候删除数据。

你想想发生什么情况?

[Test]
[ExpectedException(typeof(NHibernate.StaleObjectStateException))]
public void DeleteConcurrencyViolationCanotThrowException()
{
Customer c1 = _transaction.GetCustomerById(2);
Customer c2 = _transaction.GetCustomerById(2); _transaction.DeleteCustomerTransaction(c1);
_transaction.DeleteCustomerTransaction(c2);
}

同理,看看数据库里的数据,第二条数据不见了。

其生成SQL的查询语句同上面一样,仅仅是一条删除语句:

DELETE FROM Customer WHERE CustomerId = @p0 AND Version = @p1; @p0 = '2', @p1 = '1'

好了,这里通过两个简单的实例说明了在NHibernate中对并发控制的支持。

相信有了一定的了解,大家也能够编写一些有趣的測试来试试NHibernate中的乐观并发控制。

结语

这一篇我们初步探索了NHibernate中的并发控制,并用一个典型的实例分析了详细怎么做。我想这仅仅是蜻蜓点水,很多其它的乐趣就自己探索吧。比方在不同的Session中的并发啊,更新啊,删除啊......

NHibernate之旅(7):初探NHibernate中的并发控制的更多相关文章

  1. [转]NHibernate之旅(7):初探NHibernate中的并发控制

    本节内容 什么是并发控制? 悲观并发控制(Pessimistic Concurrency) 乐观并发控制(Optimistic Concurrency) NHibernate支持乐观并发控制 实例分析 ...

  2. NHibernate之旅(18):初探代码生成工具使用

    本节内容 引入 代码生成工具 结语 引入 我们花了大量的篇幅介绍了相关NHibernate的知识.一直都是带着大家手动编写代码,首先创建数据库架构.然后编写持久化类和映射文件,最后编写数据操作方法.測 ...

  3. [转]NHibernate之旅(6):探索NHibernate中的事务

    本节内容 事务概述 1.新建对象 [测试成功提交] [测试失败回滚] 2.删除对象 3.更新对象 4.保存更新对象 结语 上一篇我们介绍了NHibernate中的Insert, Update, Del ...

  4. NHibernate之旅系列文章导航

    NHibernate之旅系列文章导航 宣传语 NHibernate.NHibernate教程.NHibernate入门.NHibernate下载.NHibernate教程中文版.NHibernate实 ...

  5. Fluent NHibernate之旅

    Fluent NHibernate 之旅 导航篇: [原创]Fluent NHibernate之旅开篇: [原创]Fluent NHibernate之旅二--Entity Mapping: [原创]F ...

  6. [转]NHibernate之旅(10):探索父子(一对多)关联查询

    本节内容 关联查询引入 一对多关联查询 1.原生SQL关联查询 2.HQL关联查询 3.Criteria API关联查询 结语 关联查询引入 在NHibernate中提供了三种查询方式给我们选择:NH ...

  7. [转]NHibernate之旅(1):开篇有益

    本节内容 NHibernate是什么 NHibernate的架构 NHibernate资源 欢迎加入NHibernate中文社区 作者注:2009-11-06已更新 NHibernate开篇有益 学习 ...

  8. NHibernate之旅(21):探索对象状态

    本节内容 引入 对象状态 对象状态转换 结语 引入 在程序运行过程中使用对象的方式对数据库进行操作,这必然会产生一系列的持久化类的实例对象.这些对象可能是刚刚创建并准备存储的,也可能是从数据库中查询的 ...

  9. NHibernate系列文章八:NHibernate对象一级缓存

    摘要 Nhibernatea缓存非常强大,按照缓存存储在Session对象还是SessionFactory对象分为一级缓存和二级缓存. 一级缓存存在于Session对象里,也叫Session缓存,由S ...

随机推荐

  1. python--类的约束, 异常处理, MD5, 日志处理

    一 . 类的约束 1. 写一个父类,父类中的某个方法要抛出一个异常 NotImplementedError class Base: # 对子类进行了约束. 必须重写该方法 # 以后上班了. 拿到公司代 ...

  2. python--基础数据类型的补充与深浅copy

    一 . join的用法 lst =['吴彦祖','谢霆锋','刘德华'] s = '_'.join(lst) print(s) # 吴彦祖_谢霆锋_刘德华 # join() "*" ...

  3. python中strip(),lstrip(),rstrip()函数的讲解

    1. strip() 它的函数原型:string.strip(s[, chars]),它返回的是字符串的副本,并删除前导和后缀字符.(意思就是你想去掉字符串里面的哪些字符,那么你就把这些字符当参数传入 ...

  4. Java-获取一个类的父类

    如何使用代码获取一个类的父类 package com.tj; public class MyClass implements Cloneable { public static void main(S ...

  5. OSPF 提升 三 type of Areas

    ospf  ccnp 三 上图中 rip域中的不连续的100条路由   在a1中导致LSDB太大      在保证网段的可达性的前提下   尽可能减少区域内路由器的lsdb       可以将a1设置 ...

  6. [android开发篇]安装android sdk的时候请注意

    第二就是: 如果要国内镜像的话: 3.大连东软信息学院镜像服务器地址: http://mirrors.neusoft.edu.cn  端口:80 随便选择一个就行啦.这里我选择的是第三个站点,即大连东 ...

  7. 九度oj 题目1340:小A的计算器

    题目描述: 以往的操作系统内部的数据表示都是二进制方式,小A新写了一个操作系统,系统内部的数据表示为26进制,其中0-25分别由a-z表示. 现在小A要在这个操作系统上实现一个计算器,这个计算器要能实 ...

  8. cell展开的几种方式

    一.插入新的cell 原理: (1)定义是否展开,和展开的cell的下标 @property (assign, nonatomic) BOOL isExpand; //是否展开 @property ( ...

  9. 在VMWare下为CentOS设置静态IP通过NAT访问外网

    一.背景 安装好的CentOS系统默认是通过DHCP自动分配地址来共享主机的IP以达到访问外网的目的,但是因为莫名的原因无法访问外网.只好改为通过静态IP的方式访问外网. 二.操作步骤 2.1 确认开 ...

  10. 说说icon图标

    咳咳,其实我是想copy过来的,然而,他竟然是用代码写的图标... (正经脸)话说icon图标是一种网页中常用图标的一种,网络上有各式各样的应用案例,在此就不多啰嗦了.其实我也不过是用着现成的而已,所 ...