为了可以同步访问实体,JPA提供了2种锁机制。这两种机制均可以免两个事务中的其中一个,在不知情的状况下覆盖另外一个事务的数据。html
经过实体锁,咱们一般但愿避免在两个并行事务中产生以下情形:java
结果是,Adam所作的修改彻底被Barbara所覆盖掉了,可是Barbara对此却绝不知晓。像这样的状况一般被称为“脏读”。显然,咱们但愿的结果是Adam写入 XA,而Barbara须要在写入 XB以前检查对 XA 的修改。数据库
乐观锁基于的假设是实际中冲突不多发生,即便发生,抛出一个错误也比想办法避免它们更容易接受和简单。在乐观锁中,容许一个事务正确完成,但另外一个事务须要抛出异常并回滚,而且必须被从新执行或者丢弃。apache
咱们还以Adam和Barbara为例,下面是一个使用乐观锁可能发生的情形:api
如你所见,Barbara被强制要求检查Adam的修改,而且她能够选择继续修改Adam的结果并保存(合并修改)。最后的数据将同时包括Adam和Barbara的修改。oracle
乐观锁彻底由JPA控制。它须要在DB表中额外存储一个版本号列。它彻底依靠于底层用来存储关系型数据的DB引擎来工做。ui
对于某些人来讲,悲观锁更容易接受。当事务须要修改一个可能被其余事务同时修改的实体时,事务会发起一个命令将实体锁住。全部的锁会持续到事务结束后再自动释放。spa
使用悲观锁的情形可能以下所示:设计
如你所见,Barbara又一次被强制的写入 XAB,同时也包含了Adam的修改。可是,这个方案与乐观锁彻底不一样——Barbara须要等待Adam的事务完成之后才可以读取数据。更甚的是,为了让该场景正确工做,咱们须要在两个事务中都手动发起一个lock命令。(由于咱们并不肯定那个事务先运行,因此两个事务都须要在修改数据前先进行锁定)虽然乐观锁要为每一个实体增长一个版本列,比悲观锁工做略多,可是以后咱们不须要再在事务中发起锁操做了。JPA会自动完成全部的检查,咱们只须要处理可能的异常便可。code
悲观锁使用底层数据库提供的锁机制来锁住表中已有的记录。JPA须要知道如何触发这些锁,而且尚不能彻底支持某些数据库。
即便是JPA规范中也说到,不须要提供PESSIMISTIC_READ(由于许多DB只支持WRITE锁):
容许JPA实现用
LockModeType.PESSIMISTIC_WRITE
来代替LockModeType.PESSIMISTIC_READ
,可是反之不可。
首先,我想说,对于实体中有添加了@Version注解的列,JPA会自动对该实体使用乐观锁。你不须要使用任何锁命令。可是,你能够在任什么时候候发起一个如下类型的锁:
LockModeType.Optimistic
LockModeType lockMode = resolveLockMode();
A a = em.find(A.class, 1, lockMode);
LockModeType.OPTIMISTIC_FORCE_INCREMENT
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
是无效的:
在步骤4中,若是事务B没有增长版本列的值,那么就没法阻止事务A覆盖B的修改。即便事务B使用的是悲观锁,锁模式LockModeType.PESSIMISTIC_FORCE_INCREMENT
也会强制事务B更新版本号,并让事务A失败并抛出OptimisticLockException
。
为了发起一个指定类型的锁,JPA提供了如下方法:
某些EntityManager
方法接收一个指定锁类型的可选参数,例如
merge()
或者refresh()
JPA的Query接口还提供了setLockMode(LockModeType lockMode)方法来锁住全部经过查询获取的实体
你可使用JPA中这两种锁机制中的任意一种。若是须要,也能够选择悲观锁类型PESSIMISTIC_FORCE_INCREMENT
,把两者混起来用。