【Hibernate步步为营】--锁机制具体解释
上篇文章具体讨论了hql的各种查询方法。在讨论过程中写了代码演示样例。hql的查询方法类似于sql,查询的方法比較简单,有sql基础的开发者在使用hql时就会变得相当的简单。
Hibernate在操作数据库的同一时候也提供了对数据库操作的限制方法。这样的方法被称为锁机制,Hibernate提供的锁分为两种一种是乐观锁。第二种是悲观锁。
通过使用锁可以控制数据库的并发性操作。限制用户对数据库的并发性的操作。
一、锁简单介绍
锁能控制数据库的并发操作,通过使用锁来控制数据库的并发操作,Hibernate提供了两种锁来控制数据库的操作,各自是乐观锁和悲观锁。
乐观锁:大多数是採用数据版本号的方式实现,一般在数据库中增加一个version字段,在读取数据的时候将version取出来,在保存数据的时候推断version的值是否小于数据库中的version值,假设小于不会更新,否则可更新数据库。
悲观锁:一般是由数据库机制实现的,在整个过程中把数据锁住(查询时)。仅仅要事务不释放(提交/回滚)那么不论什么用户都不能查看和改动,通过使用LockMode来控制对数据库的操作。
二、乐观锁
乐观锁是通过在数据库中加入一个名为version的字段来实现每次对数据的校验,在每次操作数据库的时候会自己主动更新version的值,这样每次操作的version值是不一样的,所以假设有并发操作时将会首先校验version值是否小于数据库的version值。假设小于的话不会更新数据库,它的详细用法例如以下演示样例:
2.1 映射实体Inventory.java
该类是映射的实体类,当中的属性quantity是数字的个数,在以下演示的演示样例中通过操作quantity来更新数据库的信息,另外的version字段是数据库信息的版本。能通过校验来实现控制数据库的操作,它的详细控制方法要在映射文件里编写实现。
package com.src.hibernate; public class Inventory { //主键id,标识号
private String itemNo;
public String getItemNo() {
return itemNo;
}
public void setItemNo(String itemNo) {
this.itemNo = itemNo;
} //列表名称
private String itemName;
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
} //个数
private int quantity;
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
} //版本
private int version;
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
}
2.2 映射文件
在映射文件里须要加入<version>字段来实现对数据库版本的映射,该标签的name属性要设置为相应实体中的version字段,这样在操作数据库时它会自己主动校验数据库版本来限制对数据的操作。
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.src.hibernate.Inventory" table="t_inventory">
<id name="itemNo">
<generator class="assigned"/>
</id>
<version name="version"/>
<property name="itemName" type="string"/>
<property name="quantity" type="integer"/>
</class>
</hibernate-mapping>
2.3 数据操作
測试上面的演示样例,在程序中加入了两个方法来载入并改动数据,当中的testLoad1()方法首先载入数据库中的数据,并改动字段Quantity的值。改动后保存数据,这时要设置断点,在提交事务时设置断点,停止对数据库的提交操作。此时运行testLoad2()方法,将会发出错误信息,不能更新数据库操作。
public void testLoad1(){
Session session=null;
try{
//创建session对象
session=HibernateUtils.getSession();
//开启事务
session.beginTransaction();
Inventory inven=(Inventory)session.load(Inventory.class, "1001");
System.out.println("p1-->name:"+inven.getItemName());
inven.setQuantity(inven.getQuantity()-200);
session.save(inven);
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
public void testLoad2(){
Session session=null;
try{
//创建session对象
session=HibernateUtils.getSession();
//开启事务
session.beginTransaction();
Inventory inven=(Inventory)session.load(Inventory.class, "1001"); System.out.println("p2-->name:"+inven.getItemName());
inven.setQuantity(inven.getQuantity()-200);
session.save(inven);
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
在不停止testLoad1()方法时运行testLoad2()方法。Hibernate发出例如以下语句,该语句说明testLoad2已经改动了数据库中的数据,可是testLoad1也在改动仅仅是没有提交。
Hibernate: select inventory0_.itemNo as itemNo0_0_, inventory0_.version as version0_0_, inventory0_.itemName as itemName0_0_, inventory0_.quantity as quantity0_0_ from t_inventory inventory0_ where inventory0_.itemNo=?
p2-->name:zhangsan
Hibernate: update t_inventory set version=?, itemName=?, quantity=? where itemNo=? and version=?
testLoad2()提交更改后,接下来执行testLoad1()的断点,将会发出:Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) 的错误,说明两个同一时候在改动同一条数据。导致了并发操作。发出了错误提示。
三、悲观锁
悲观锁和乐观锁不同。它是通过数据库机制限制对数据库的操作的,通过用法的LockMode參数来配置对数据库的并发操作。在一次訪问过程中将会把数据锁住(查询时),仅仅要事务不提交那么不论什么用户都不能查看和改动,其他用户操作时将会被堵塞。不能同一时候操作,须要等待第一次訪问完毕后再进行其他的操作。详细方法例如以下演示样例。
3.1 实体Inventory.java
实体内容和乐观锁同样,不同的是在实体中不须要加入版本属性,由于它不是通过推断版本来限制操作的。
package com.src.hibernate; public class Inventory { //主键id,标识号
private String itemNo;
public String getItemNo() {
return itemNo;
}
public void setItemNo(String itemNo) {
this.itemNo = itemNo;
} //列表名称
private String itemName;
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
} //个数
private int quantity;
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
}
3.2 映射文件
实体相应的映射文件也相当的简单,仅仅须要配置相应的映射就可以,没有其他复杂的操作。例如以下代码:
<? xml version="1.0"? >
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.src.hibernate.Inventory" table="t_inventory">
<id name="itemNo">
<generator class="assigned"/>
</id> <property name="itemName" type="string"/>
<property name="quantity" type="integer"/>
</class>
</hibernate-mapping>
3.3 測试代码
这里採用两个方法来測试操作,当中的testLoad1()方法首先查询数据然后改动数据,testLoad2()也是查询和改动数据,例如以下代码:
public void testLoad1(){
Session session=null;
try{
//创建session对象
session=HibernateUtils.getSession();
//开启事务
session.beginTransaction();
Inventory inven=(Inventory)session.load(Inventory.class, "1001",LockMode.UPGRADE);
System.out.println("p1-->name:"+inven.getItemName()); inven.setQuantity(inven.getQuantity()-200);
session.save(inven);
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
public void testLoad2(){
Session session=null;
try{
//创建session对象
session=HibernateUtils.getSession();
//开启事务
session.beginTransaction();
Inventory inven=(Inventory)session.load(Inventory.class, "1001",LockMode.UPGRADE); System.out.println("p2-->name:"+inven.getItemName());
inven.setQuantity(inven.getQuantity()-200);
session.save(inven);
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
session.getTransaction().rollback();
}finally{
HibernateUtils.closeSession(session);
}
}
演示并发操作时在testLoad1()方法的打印处设置对应的断点,然后启动Debug调试。执行到断点后停止,此时执行testLoad2()方法,实现了对数据库的并发操作。在执行testLoad2()方法时Hibernate发出了SQL语句,可是却一直不能查询和检索数据,由于数据库中加了悲观锁。数据库限制了并发性的操作,所以Hibernate仅仅是发出了命令可是一直没有操作权限,也就是说testLoad2()方法在执行时遭到了堵塞。操作被压到堆栈中等待上次的完毕。当testLoad1()方法执行完后。将会从堆栈中继续获取testLoad2()的操作命令。这样的乐观锁不仅限制了对数据的操作并且还能保证数据的完整性。
结语
Hibernate的锁机制限制了数据库的并发性的操作,功能非常强大,在使用时建议使用悲观锁。由于悲观锁使用的是数据库的机制,限制彻底。并且能确保数据的完整性。在使用这两种锁时各有优缺点,大数据量建议採用乐观锁,它会直接提示错误,性能上会高于悲观锁,悲观锁会加大数据库的负担,大数据量的话easy出现故障。
【Hibernate步步为营】--锁机制具体解释的更多相关文章
- Hibernate学习---第十二节:Hibernate之锁机制&乐观锁实现
1.悲观锁 它指的是对数据被外界修改保持保守态度,因些,在整个数据处理过程中,将数据牌锁定状态.悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层的锁机制才能保证数据访问的排他性,否则,即使在本 ...
- Hibernate中的锁机制
锁机制:是数据库为了保证数据的一致性<一个事务的各种操作不相互影响>而使各种共享资源在被并发访问访问变得有序所设计的一种规则,用来保证在当前用户进行操作数据的时候其他的用户不能对同一数据进 ...
- 【Hibernate步步为营】--映射合集汇总
前几篇文章具体讨论了对象模型到关系模型的转化方法,对映射关系做了具体的了解,Hibernate将对象模型转化为对应的关系模型是通过使用对应的映射来完毕的(相同也能够使用注解),对于对象之间的关系的转化 ...
- Hibernate锁机制
业务逻辑的实现过程中,往往需要保证数据访问的排他性.因此,我们就需要通过一些机制来保证这些数据在某个操作过程中不会被外界修改,这样的机制,在这里,也就是所谓的“锁”,即给我们选定的目标数据上锁,使其无 ...
- 数据库并发及锁机制及Hibernate锁实现
数据库事务的定义 数据库事务(Database Transaction),是指作为单个逻辑工作单元执行的一系列操作.一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性.一致性.隔离性和持久性) ...
- InnoDB锁机制分析
InnoDB锁机制常常困扰大家,不同的条件下往往表现出不同的锁竞争,在实际工作中经常要分析各种锁超时.死锁的问题.本文通过不同条件下的实验,利用InnoDB系统给出的各种信息,分析了锁的工作机制.通过 ...
- [转载] 数据库分析手记 —— InnoDB锁机制分析
作者:倪煜 InnoDB锁机制常常困扰大家,不同的条件下往往表现出不同的锁竞争,在实际工作中经常要分析各种锁超时.死锁的问题.本文通过不同条件下的实验,利用InnoDB系统给出的各种信息,分析了锁的工 ...
- oracle锁机制
1 前言 数据库大并发操作要考虑死锁和锁的性能问题.看到网上大多语焉不详(尤其更新锁),所以这里做个简明解释,为下面描述方便,这里用T1代表一个数据 库执行请求,T2代表另一个请求,也可以理解为T1为 ...
- Redis 利用锁机制来防止缓存过期产生的惊群现象-转载自 http://my.oschina.net/u/1156660/blog/360552
首先,所谓的缓存过期引起的“惊群”现象是指,在大并发情况下,我们通常会用缓存来给数据库分压,但是会有这么一种情况发生,那就是在一定时间 内生成大量的缓存,然后当缓存到期之后又有大量的缓存失效,导致后端 ...
随机推荐
- python3实践-从网站获取数据(Carbon Market Data-BJ) (pandas,bs4)
自己边看边实践一些简单的实际应用,下面的程序是从某个网站上获取需要的数据. 在编写的过程中,通过学习陆续了解到一些方法,发现Python真的是很便捷. 尤其是用pandas获取网页中的表格数据,真的是 ...
- Python开发基础-Day15正则表达式爬虫应用,configparser模块和subprocess模块
正则表达式爬虫应用(校花网) import requests import re import json #定义函数返回网页的字符串信息 def getPage_str(url): page_stri ...
- Python开发基础-Day5-字符编码、文件处理和函数基础(草稿)
字符编码 为什么要有字符编码? 字符编码是为了让计算机能识别我们人写的字符,因为计算机只认识高低电平,也就是二进制数"0","1". 一个文件用什么编码方式存储 ...
- 【BZOJ 3530】【SDOI 2014】数数
http://www.lydsy.com/JudgeOnline/problem.php?id=3530 上午gty的测试题,爆0了qwq 类似文本生成器那道题,把AC自动机的转移建出来,准确地说建出 ...
- [BZOJ1444]有趣的游戏(AC自动机+矩阵乘法)
n个等长字符串,机器会随机输出一个字符串(每个字母出现的概率为p[i]),问每个字符串第一个出现的概率是多少. 显然建出AC自动机,套路地f[i][j]表示i时刻位于节点j的概率. 构建转移矩阵,当i ...
- MathType中带上下标字符不对其
如图,上面的好看,下面的就不好看的. 上面的图使用下图下面的形式,下面的图是用的是上面的形式. 如图可以看出,右侧的更好. 比如UiTVj这样的,需要分别都用下面的形式,不能UiT用上面的,Vj直接输 ...
- Inno Setup入门(二十一)——Inno Setup类参考(7)
复选框 复选框(CheckBox)用于多个并不互斥的几个选项中作出一个或者多选择,例如字体可以有粗体.斜体和下划线,这三种状态可以任意组合,像这样的选项可以采用复选框实现.Pascal脚本中对应的类是 ...
- .net 4.5如何使用Async和Await进行异步编程
通过使用异步编程,可避免出现性能瓶颈,并提高应用程序的整体响应.然而,技术编写异步应用程序的传统方法过于复杂,这使得异步程序难以编写,调试和维护. Visual Studio2012引入了一个简单的开 ...
- Wishbone接口Altera JTAG UART
某些时候,我们在使用Altera FPGA的时候,尤其是涉及SoC系统的时候,通常需要一个串口与PC交互.使用Altera的USB-Blaster免去了外接一个串口.我们可以使用下面所述的IP核通过U ...
- centos7 安装selenium和firefox
之前有一篇文章介绍过在ubuntu下安装selenium和firefox 现在介绍下centos7 注意以下都是下载的linux64位的软件,32位的请自己找下链接, 现在使用的python的版本是3 ...