【Hibernate 9】悲观锁和乐观锁
一、锁的基本简介
1.1,为什么需要锁
首先,锁的概念产生,主要是为了解决并发性的问题。什么是并发性问题呢,比如:
Angel现在银行有个账号,里面有存款1000块。现在,Angel的账户,在两个地方分别执行操作。首先,Angel妈妈拿着主卡在四川给Angel账户存钱,读取出Angel的余额是1000块。其次,Angel拿着副卡在廊坊从账户里取钱,读取出angel 的余额是1000块。这时候,angel取了200块钱,余额变为1000-200=800块。可是angel妈妈执行存钱1000块操作,Angel余额变为1000+1000=2000(Angel妈妈最初读取出的余额为1000)这时候,Angel的余额就出现了问题,按照正常逻辑,账户余额为1000-200+1000=1800块。这个问题,就叫做更新丢失。Angel的账户,丢失了取款200的更新。为了解决这一个问题,我们需要引入锁的概念
1.2,悲观锁
悲观锁:通常是有数据库机制实现的,在整个过程中把数据锁住(查询),只要事务不释放(提交 / 回滚),那么任何用户都不能查看或修改。
正如上面的例子,Angel妈妈在四川,读取出了Angel的余额,那么angel的账户就被锁定了。angel就不能在廊坊对其账户进行操作,只有等到angel妈妈对这个账户的更新结束,也就是正常更新余额1000+1000=2000的业务结束,angel才能在廊坊执行取款操作。那么,悲观锁对外界的修改持保守态度,有效的保证了数据的一致性。但是,优点:它不适合多个用户并发访问。当一个锁住的资源不被释放掉的时候,这个资源永远不会被其他用户进行修改,容易造成无限期的等待,也就是等待超时。(想象一下,angel妈妈一直没有执行完存钱操作,angel取钱的道路该是多么的艰辛。。。或者说angel一直没有执行完取钱操作,angel妈妈的存钱道路该有多么心酸。。。)
那么,怎样能保证数据的一致性,又不会导致无期限的等待呢?
1.3,乐观锁
乐观锁,从本质上来说并不是一种锁,它大多数的使用是采用数据版本的方式(version)实现,一般在数据库中加入一个version字段,在读取数据的时候将version读取出来,在保存数据的时候判断version的值是否小于数据库中的version值,如果小于不予更新,否则给予更新。
如果运用乐观锁的实现机制去解决Angel的取款问题,则会发生什么呢?首先,angel妈妈在四川读出angel的余额1000,和其数据的版本号1;同时angel在廊坊也读取出了余额1000,和版本号1。这时候angel执行了取款操作200,更新余额为1000-200=800,同时将数据版本号更新为2。这时候angel妈妈执行存钱操作,而版本号1<2,所以不予以执行1000+1000=2000的更新操作。若是要存款,只能再次执行业务流程,这样,保证了数据的一致性。
1.4,Hibernate的加锁模式
A: LockMode.NONE : 无锁机制。
B:LockMode.WRITE : Hibernate 在 Insert 和 Update 记录的时候会自动获取。
C:LockMode.READ : Hibernate 在读取记录的时候会自动获取。
以上这三种锁机制一般由 Hibernate 内部使用,如 Hibernate 为了保证 Update过程中对象不会被外界修改,会在 save 方法实现中自动为目标对象加上 WRITE 锁。
D: LockMode.UPGRADE :利用数据库的 for update 子句加锁。
E: LockMode. UPGRADE_NOWAIT : Oracle 的特定实现,利用 Oracle 的 for update nowait 子句实现加锁。
二、实例分析Hibernate的锁机制
2.1,乐观锁
<span style="font-family:KaiTi_GB2312;font-size:18px;"><?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.angel.hibernate.Inventory" table="t_inventory" <span style="color:#ff0000;">optimistic-lock="version"</span>>
<id name="itemNo">
<generator class="assigned"/>
</id>
<version name="version"/>
<property name="itemName"/>
<property name="quantity"/>
</class>
</hibernate-mapping></span>
测试类:
<span style="font-family:KaiTi_GB2312;font-size:18px;">package test.com.angel.hibernate; import junit.framework.TestCase; import org.hibernate.Session; import com.angel.hibernate.HibernateUtils;
import com.angel.hibernate.Inventory; public class OptimisticLockingTest extends TestCase { public void testLoad1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Inventory inv = (Inventory) session.load(Inventory.class, "1002");
System.out.println("opt1-->itemNo=" + inv.getItemNo());
System.out.println("opt1-->itemName=" + inv.getItemName());
System.out.println("opt1-->version=" + inv.getVersion());
System.out.println("opt1-->quantity=" + inv.getQuantity()); inv.setQuantity(inv.getQuantity() - 200); session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
HibernateUtils.closeSession(session);
}
} public void testLoad2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Inventory inv = (Inventory) session.load(Inventory.class, "1002");
System.out.println("opt2-->itemNo=" + inv.getItemNo());
System.out.println("opt2-->itemName=" + inv.getItemName());
System.out.println("opt2-->version=" + inv.getVersion());
System.out.println("opt2-->quantity=" + inv.getQuantity()); inv.setQuantity(inv.getQuantity() + 200); session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
HibernateUtils.closeSession(session);
}
} }
</span>
当我们在方法1读取数据结束,但是未提交事务之前,紧接着执行方法2的时候,由于version字段的值被更改,所以会导致方法1执行不通过,从而保证了数据的一致性。
2.2,悲观锁
<span style="font-family:KaiTi_GB2312;font-size:18px;">package test.com.angel.hibernate; import junit.framework.TestCase; import org.hibernate.LockMode;
import org.hibernate.Session; import com.angel.hibernate.HibernateUtils;
import com.angel.hibernate.Inventory; public class OptimisticLockingTest extends TestCase { public void testLoad1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Inventory inv = (Inventory) session.load(Inventory.class, "1002",LockMode.UPGRADE);
System.out.println("opt1-->itemNo=" + inv.getItemNo());
System.out.println("opt1-->itemName=" + inv.getItemName());
System.out.println("opt1-->version=" + inv.getVersion());
System.out.println("opt1-->quantity=" + inv.getQuantity()); inv.setQuantity(inv.getQuantity() - 200); session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
HibernateUtils.closeSession(session);
}
} public void testLoad2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Inventory inv = (Inventory) session.load(Inventory.class, "1002",LockMode.UPGRADE);
System.out.println("opt2-->itemNo=" + inv.getItemNo());
System.out.println("opt2-->itemName=" + inv.getItemName());
System.out.println("opt2-->version=" + inv.getVersion());
System.out.println("opt2-->quantity=" + inv.getQuantity()); inv.setQuantity(inv.getQuantity() + 200); session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
HibernateUtils.closeSession(session);
}
} }
</span>
当我们在方法1读取数据结束,但是未提交事务之前,紧接着执行方法2的时候,由于添加悲观锁的缘故,方法2无法执行,只有当方法1进行了提交,方法2才能继续执行。
三、总结
加锁可以有效的解决并发问题,但是,这也得根据应用程序的具体情况而定。如果开发 的应用程序都是单人操作,那么根本就不必引入锁的概念。在这里,顺便总结一下数据中锁的基本分类:
共享 (S) 用于不更改或不更新数据的操作(只读操作),如 SELECT 语句。
更新 (U) 用于可更新的资源中。防止当多个会话在读取、锁定以及随后可能进行的资源更新时发生常见形式的死锁。
排它 (X) 用于数据修改操作,例如 INSERT、UPDATE 或 DELETE。确保不会同时同一资源进行多重更新。
【Hibernate 9】悲观锁和乐观锁的更多相关文章
- Hibernate解决高并发问题之:悲观锁 VS 乐观锁
高并发问题是程序设计所必须要解决的问题,解决此类问题最主要的途径就是对对程序进行加锁控制.hibernate对加锁机制同样做出了实现,常用加锁方式为悲观锁和乐观锁.悲观锁指的是对数据被外界(包括本系统 ...
- 025 hibernate悲观锁、乐观锁
Hibernate谈到悲观锁.乐观锁,就要谈到数据库的并发问题,数据库的隔离级别越高它的并发性就越差 并发性:当前系统进行了序列化后,当前读取数据后,别人查询不了,看不了.称为并发性不好 数据库隔离级 ...
- Hibernate 再接触 悲观锁和乐观锁
为什么取1248 二进制 CRUD 移位效率高 在并发和效率选择一个平衡点 一般不会考虑幻读 因为我们不会再一个事务里查询两次,(只能设置为seralizable) 悲观锁和乐观锁的前提是read-u ...
- Hibernate的悲观锁和乐观锁
前一篇博客我们从数据库角度分析,锁可以分为三种,分别为共享锁,独占锁和更新锁.我们从程序的角度来看锁可以分为两种类型,悲观锁和乐观锁,Hibernate提供对这两种锁 的支持,我们来了解一下Hiber ...
- mysql-mysql悲观锁和乐观锁
1.mysql的四种事务隔离级别 I. 对于同时运行多个事务,当这些事务访问数据库中的相同数据时,如果没有采取必要的隔离机制,就会导致各种并发问题. (1)脏读: 对于两个事物 T1, T2, T1 ...
- Oracle数据库悲观锁与乐观锁详解
数据的锁定分为两种方法,第一种叫做悲观锁,第二种叫做乐观锁.什么叫悲观锁呢,悲观锁顾名思义,就是对数据的冲突采取一种悲观的态度,也就是说假设数据肯定会冲突,所以在数据开始读取的时候就把数据锁定住.而乐 ...
- MySQL学习笔记(四)悲观锁与乐观锁
恼骚 最近在搞并发的问题,订单的异步通知和主动查询会存在并发的问题,用到了Mysql数据库的 for update 锁 在TP5直接通过lock(true),用于数据库的锁机制 Db::name('p ...
- 谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?
在日常开发中,尤其是业务开发,少不了利用 Java 对数据库进行基本的增删改查等数据操作,这也是 Java 工程师的必备技能之一.做好数据操作,不仅仅需要对 Java 语言相关框架的掌握,更需要对各种 ...
- Oracle的悲观锁和乐观锁
为了得到最大的性能,一般数据库都有并发机制,不过带来的问题就是数据访问的冲突.为了解决这个问题,大多数数据库用的方法就是数据的锁定. 数据的锁定分为两种方法,第一种叫做悲观锁,第二种叫做乐观锁.什么叫 ...
- (转载)Oracle的悲观锁和乐观锁
为了得到最大的性能,一般数据库都有并发机制,不过带来的问题就是数据访问的冲突.为了解决这个问题,大多数数据库用的方法就是数据的锁定. 数据的锁定分为两种方法,第一种叫做悲观锁,第二种叫做乐观锁.什么叫 ...
随机推荐
- TFS Build Error: CSC : fatal error CS0042: Unexpected error creating debug information file 'xxxx.PDB'
CSC : fatal error CS0042: Unexpected error creating debug information file 'xxxx.PDB' -- 'c:\Builds\ ...
- 加入ScrollView后OnGestureListener无效的解决办法
android中,ViewFlipper+OnGestureListener可以实现左右滑动效果. 但是在ViewFlipper加上了ScrollView就悲剧了,左右滑动事件无效了…… 这里其实只需 ...
- SharedPreferences实现自动登录记住用户名密码
最近Android项目需要一个自动登录功能,完成之后,特总结一下,此功能依靠SharedPreferences进行实现. SharedPreferences简介 SharedPreferences ...
- ibatis插入数据后返回自增长的主键
insert into testTable ( activity_id,activity_title values ( #{activityId,jdbcType=INTEGER}, #{activi ...
- JAVA 对象的转型
/* 对象的转型: 1.对象的向上转型 子类转成父类 默认进行 父类引用指向子类对象 2.对象的向下转型 父类转成子类 强制进行 关键字:instanceof 测试左边对象是否是右边类的实例 如果是返 ...
- 常见的http头信息
请求头:用于告诉服务器,客户机支持的数据类型 accept-charset:用于告诉服务器,客户机采用的编码 accept-Encoding:用于告诉服务器,客户机支持的数据压缩格式 Host:客户机 ...
- svn: Can't convert string from 'UTF-8' to native encoding 的解决办法
http://www.leakon.com/archives/610 http://www.toplee.com/blog/566.html http://svnbook.red-bean.com/e ...
- 根据职位名,自动生成jd
代码本身就是最好的解释,不赘述. 文本聚类输出: cluster.py #!/usr/bin/env python # coding=utf-8 import jieba,re from gensim ...
- spring mvc中的json整合
spring mvc整合过程中是有版本兼容的问题.具体的哪个版本的springmvc和哪个个版本的json包冲突我也无从考证了.我用的springmvc版本是3.2.1jaskson的版本是 1.1. ...
- Inno Setup中做补丁通过注册表获取原程序安装目录
今天找VM补丁看到的,是个innosetup封装的,所以习惯性的喜欢去看人家的iss文件是怎么编写的. DefaultDirName={reg:HKLM\SOFTWARE\VMware%2c%20In ...