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共享.这一节介绍二级缓存其他两个方面:二级缓存查询和二级缓 ...
随机推荐
- 2016 - 1 - 24 NSURLSession (一)
一: NSURLSession简介 1.实施步骤 1.1 使用 NSURLSession对象 创建TASK ,然后执行TASK 2.TASK的类型: 二: NSURLSession的简单使用: - ( ...
- socket_server源码剖析、python作用域、IO多路复用
本节内容: 课前准备知识: 函数嵌套函数的使用方法: 我们在使用函数嵌套函数的时候,是学习装饰器的时候,出现过,由一个函数返回值是一个函数体情况. 我们在使用函数嵌套函数的时候,最好也这么写. def ...
- 或许是 Nginx 上配置 HTTP2 最实在的教程了
导读 从 2015 年 5 月 14 日 HTTP/2 协议正式版的发布到现在已经快有一年了,越来越多的网站部署了 HTTP2,HTTP2 的广泛应用带来了更好的浏览体验,只要是 Modern 浏览器 ...
- include指令和include标签的区别
区别 类别 语法 发生作用时间 包含的内容 转化成Servlet 编译时间 运行时间 include指令 <%@ include file="" %> 页面交换 实际内 ...
- jsp打印页面 js代码
function doPrint() { bdhtml=window.document.body.innerHTML; sprnstr=""; //开始打印标识字符串有17个字符 ...
- 【Gerrit】gerrit server搭建
Part 1 Gerrit Prerequisites: 1.Java JDK>1.7 2.Git 3.SSH server 4.DB part 2 Set local gerrit serv ...
- Android学习七:new Date使用
1.例子 学习时间函数,并实现了简单的多个按钮监听同一个事件的方法 2.代码 代码很简单,也很清晰 package com.example.datetime; import java.text.Sim ...
- Java条形码生成技术-Barcode4j
背景 目前二维码的应用场景已经遍布各类互联网平台,通常是将产品/商品的唯一编号存储于二维码中以做扫码识别. 而用于生产环境的条形码技术仍然存在,如硬件设备制造.供应.物流运输等等. 在常见的产品信息管 ...
- CSS之利用text-indent隐藏文字用图片当Login
html: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w ...
- Oracle表空间不足
Oracle临时表空间不足 -- 用户的缺省表空间.临时表空间 select t.username, t.default_tablespace, t.temporary_tablespace from ...