JPA的锁机制
JPA 各种实体锁模式的区别
为了能够同步访问实体,JPA提供了2种锁机制。这两种机制都可以避免两个事务中的其中一个,在不知情的情况下覆盖另一个事务的数据。
通过实体锁,我们通常希望避免在两个并行事务中产生如下情形:
- Adam的事务读取数据 X
- Barbara的事务读取数据 X
- Adam的事务修改数据 X,并将其修改为 XA
- Adam的事务写入数据 XA
- Barbara的事务修改数据 X,并将其修改为 XB
- Barbara的事务写入数据 XB
结果是,Adam所做的修改完全被Barbara所覆盖掉了,但是Barbara对此却毫不知晓。像这样的情况通常被称为“脏读”。显然,我们希望的结果是Adam写入 XA,而Barbara需要在写入 XB之前检查对 XA 的修改。
乐观锁的工作原理
乐观锁基于的假设是实际中冲突很少发生,即使发生,抛出一个错误也比想办法避免它们更容易接受和简单。在乐观锁中,允许一个事务正确完成,但另一个事务需要抛出异常并回滚,并且必须被重新执行或者丢弃。
我们还以Adam和Barbara为例,下面是一个使用乐观锁可能发生的情形:
- Adam的事务读取数据 X
- Barbara的事务读取数据 X
- Adam的事务修改数据 X,并将其修改为 XA
- Adam的事务写入数据 XA
- Barbara的事务修改数据 X,并将其修改为 XB
- Barbara的事务试图写入数据 XB,但是收到一个错误
- Barbara需要读取数据 XA(或者重新开始一个新的事务)
- Barbara的事务修改数据 XA,并将其修改为 XAB
- Barbara的事务写入数据 XAB
如你所见,Barbara被强制要求检查Adam的修改,并且她可以选择继续修改Adam的结果并保存(合并修改)。最后的数据将同时包括Adam和Barbara的修改。
乐观锁完全由JPA控制。它需要在DB表中额外存储一个版本号列。它完全依靠于底层用来存储关系型数据的DB引擎来工作。
悲观锁的工作原理
对于某些人来说,悲观锁更容易接受。当事务需要修改一个可能被其他事务同时修改的实体时,事务会发起一个命令将实体锁住。所有的锁会持续到事务结束后再自动释放。
使用悲观锁的情形可能如下所示:
- Adam的事务读取数据 X
- Adam的事务锁住 X
- Barbara的事务希望读取数据 X,但是因为 X 已经被锁住,只好等待
- Adam的事务修改数据 X,并将其修改为 XA
- Adam的事务写入数据 XA
- Barbara的事务读取数据 XA
- Barbara的事务修改数据 XA,并将其修改为 XAB
- Barbara的事务写入数据 XAB
如你所见,Barbara又一次被强制的写入 XAB,同时也包含了Adam的修改。但是,这个方案与乐观锁完全不同——Barbara需要等待Adam的事务完成以后才能够读取数据。更甚的是,为了让该场景正确工作,我们需要在两个事务中都手动发起一个lock命令。(因为我们并不确定那个事务先运行,所以两个事务都需要在修改数据前先进行锁定)虽然乐观锁要为每个实体增加一个版本列,比悲观锁工作略多,但是之后我们不需要再在事务中发起锁操作了。JPA会自动完成所有的检查,我们只需要处理可能的异常即可。
悲观锁使用底层数据库提供的锁机制来锁住表中已有的记录。JPA需要知道如何触发这些锁,并且尚不能完全支持某些数据库。
即使是JPA规范中也说到,不需要提供PESSIMISTIC_READ(因为许多DB只支持WRITE锁):
允许JPA实现用
LockModeType.PESSIMISTIC_WRITE来代替LockModeType.PESSIMISTIC_READ,但是反之不可。
JPA中可用的锁类型
首先,我想说,对于实体中有添加了@Version注解的列,JPA会自动对该实体使用乐观锁。你不需要使用任何锁命令。但是,你可以在任何时候发起一个以下类型的锁:
LockModeType.Optimistic- 这就是默认的锁类型。也是如ObjectDB所说通常被大家所忽略的锁类型。在我的印象中,只有在需要动态获取并传递锁类型时,才会用到它,即使我们很清楚最后的锁是OPTIMISTIC的。虽然这个例子不太恰当,但是一个好的API设计,即使是默认值也应该为其提供一个可选项。
- 示例:Java
LockModeType lockMode = resolveLockMode();
A a = em.find(A.class, 1, lockMode);
LockModeType.OPTIMISTIC_FORCE_INCREMENT- 这个选项很少被用到。但是如果你希望用另一个实体来锁住对当前实体的引用,就需要使用它。换句话说,即使当前实体没有被修改,但是其他实体可能因为当前实体被修改,你就可以用它来锁住对当前实体的引用。
- 示例:
- 假设我们有两个实体“书(Book)”和“书架(Shelf)”。我们可以将书添加到书架中,但是书不持有对其书架的引用。我们需要对所有移动书到其他书架的动作加锁,以避免一本书被放在2个书架上。为了锁住这个动作,光锁住当前的书架实体是不够的,因为书可能还没有放到某个书架上。锁住所有书架也不合理,因为他们在不同的事务中可能都是不同的。唯一合理的是锁住书实体本身,即使在我们这个例子中它并没有发生变化(因为它并不持有其书架的引用)。
LockModeType.PESSIMISTIC_READ- 这个模式类似于
LockModeType.PESSIMISTIC_WRITE,但是有一点不同:如果没有事务对实体加写锁,那么就不能阻塞对该实体的读取。它还允许其他事务使用LockModeType.PESSIMISTIC_READ来加锁。WRITE锁和READ锁之间的区别,已经被这两篇文章(here (ObjectDB) 和 here (OpenJPA))很详细的说明了。但是,不仅因为规范中允许,而且许多实现也没有分开处理,所以该锁模式经常被等价于LockModeType.PESSIMISTIC_WRITE。
- 这个模式类似于
LockModeType.PESSIMISTIC_WRITE- 这是
LockModeType.PESSIMISTIC_READ的增强版。当WRITE锁发生时,JPA在数据库的帮助下,会阻止其他事务读取实体,而不像READ锁那样只禁止写入。
- 这是
LockModeType.PESSIMISTIC_FORCE_INCREMENT这是另一个很少使用的锁模式。但是,它可以用来结合
PESSIMISTIC和OPTIMISTIC时使用。在以下场景中,单独使用PESSIMISTIC_WRITE是无效的:- 事务A使用乐观锁并读取实体E
- 事务B请求实体E上的WRITE锁
- 事务B提交并释放E上的锁
- 事务A更新E并提交
在步骤4中,如果事务B没有增加版本列的值,那么就无法阻止事务A覆盖B的修改。即使事务B使用的是悲观锁,锁模式
LockModeType.PESSIMISTIC_FORCE_INCREMENT也会强制事务B更新版本号,并让事务A失败并抛出OptimisticLockException。
为了发起一个指定类型的锁,JPA提供了以下方法:
某些
EntityManager方法接收一个指定锁类型的可选参数,例如- find(Class entityClass, Object primaryKey, LockModeType lockMode)
- lock(Object entity, LockModeType lockMode)
- 以及其他从DB获取数据的方法,例如
merge()或者refresh()
JPA的Query接口还提供了setLockMode(LockModeType lockMode)方法来锁住所有通过查询获取的实体
你可以使用JPA中这两种锁机制中的任意一种。如果需要,也可以选择悲观锁类型PESSIMISTIC_FORCE_INCREMENT,把二者混起来用。
JPA的锁机制的更多相关文章
- 【快学springboot】8.JPA乐观锁OptimisticLocking
介绍 当涉及到企业应用程序时,正确地管理对数据库的并发访问是至关重要的.为此,我们可以使用Java Persistence API提供的乐观锁定机制.它导致在同一时间对同一数据进行多次更新不会相互干扰 ...
- Hibernate中的锁机制
锁机制:是数据库为了保证数据的一致性<一个事务的各种操作不相互影响>而使各种共享资源在被并发访问访问变得有序所设计的一种规则,用来保证在当前用户进行操作数据的时候其他的用户不能对同一数据进 ...
- Linux 2.6内核中新的锁机制--RCU
转自:http://www.ibm.com/developerworks/cn/linux/l-rcu/ 一. 引言 众所周知,为了保护共享数据,需要一些同步机制,如自旋锁(spinlock),读写锁 ...
- MySQL学习笔记十六:锁机制
1.数据库锁就是为了保证数据库数据的一致性在一个共享资源被并发访问时使得数据访问顺序化的机制.MySQL数据库的锁机制比较独特,支持不同的存储引擎使用不同的锁机制. 2.MySQL使用了三种类型的锁机 ...
- 传智播客JavaWeb day11--事务的概念、事务的ACID、数据库锁机制、
1. 什么叫做事务? 2.默认情况下每一条sql语句都是一个事务,然后自动提交事务 ps:如果想多条语句占一个事务,则可以手动设置SetAutoCommit为false 3.关键字 start tr ...
- JUC.Lock(锁机制)学习笔记[附详细源码解析]
锁机制学习笔记 目录: CAS的意义 锁的一些基本原理 ReentrantLock的相关代码结构 两个重要的状态 I.AQS的state(int类型,32位) II.Node的waitStatus 获 ...
- Atitit.软件与编程语言中的锁机制原理attilax总结
Atitit.软件与编程语言中的锁机制原理attilax总结 1. 用途 (Db,业务数据加锁,并发操作加锁.1 2. 锁得类型 排它锁 "互斥锁 共享锁 乐观锁与悲观锁1 2.1. 自旋锁 ...
- MySQL锁机制总结(二)
前言: Mysql是一个支持插件式存储引擎的数据库系统,本文讨论的锁机制也主要包含两部分SERVER层的锁和存储引擎的锁,存储引擎是指innodb,其它存储引暂不讨论. 1. 数据库中锁相关的基本概念 ...
- Mysql事务,并发问题,锁机制
.什么是事务 事务是一条或多条数据库操作语句的组合,具备ACID,4个特点. 原子性:要不全部成功,要不全部撤销 隔离性:事务之间相互独立,互不干扰 一致性:数据库正确地改变状态后,数据库的一致性约束 ...
随机推荐
- mysql中date_add()函数的使用?
需求描述: 在使用mysql的过程中,需要对日期进行计算,比如对某个日期加上几天,几个小时等操作, 在此记录下,date_add()函数的使用. 操作过程: date_add()函数语法: DATE_ ...
- mysql中参数low_case_table_name的使用?不同参数值的设置有什么影响?
需求描述: 今天一个同事问,在mysql中,默认的表名是大小写区分的吗,默认是什么设置, 如果要设置成大小写不区分的改怎么设置,是否需要进行重启.然后就进行了查询, 对于lower_case_tabl ...
- PHP易混淆函数的区别及用法汇总
本文实例分析了PHP易混淆函数的区别及用法.分享给大家供大家参考.具体分析如下: 1.echo和print的区别PHP中echo和print的功能基本相同(输出),但是两者之间还是有细微差别的.ech ...
- 谈谈入门iOS的经验吧
前言 近期忙完项目比較闲,想写一篇博客来分享一些自学iOS的心得体会,希望对迷茫的你有所帮助.博主非科班出身,一些计算机术语上的不专业欢迎大家指正. 我是学微电子的.大四的时候找了一家深圳的专业对口的 ...
- JSP面试知识
JSP方面 1. JSP四种范围是什么?区别是什么? Page:指单单一页jsp page的范围: Request:的范围只在一jsp页发出请求到另一页之间,随后这个属性失效: Session:范围是 ...
- Dubbo -- 系统学习 笔记 -- 入门
Dubbo -- 系统学习 笔记 -- 目录 入门 背景 需求 架构 用法 入门 背景 随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行, ...
- Python学习--判断变量的数据类型
import types aaa = 0 print type(aaa) if type(aaa) is types.IntType: print "the type of aaa is i ...
- commons-beanutils的使用
commons-beanutils是通过内省来完成的. 需要两个包: commons-beanutils-1.8.3.jar commons-logging-1.1.1.jar JavaBean类: ...
- 数字图像处理笔记与体会(一)——matlab编程基础
最近开始学习数字图像处理,使用matlab实现,下面我就来记录笔记和体会,一方面是给大家提供参考,另一方面是防止我忘记了. 复习一下: 1.数字图像是用一个数字矩阵来表示的,数字阵列中的每个数字,表示 ...
- mysql基础知识笔记
Mysql基础笔记 环境配置 基本概念 mysql命令行 进入 use show 查询 select order by where like 数据过滤regexp concat 文本函数 日期函数 数 ...