JPA规范中,Lock Mode的值中包含了Optimistic, 一看就知道是用于乐观锁控制, 但是我一直被这个问题困扰,原因在于,在Entity中,已经使用@Version做了乐观锁控制,Hibernate会自动更新比对,version值,那么设置Lock Mode 为Optimistic又有何用处呢?
先看一段代码

    @PersistenceContextEntityManager entityManager;@Transactional()public Order t3(String str) {Order order67 = entityManager.find(Order.class, 67L, LockModeType.OPTIMISTIC);order67.setCancelReason(str);return order67;}

这段代码中使用了entity manager查询order,并且指定了乐观锁,然后在更新order,最初我以为这段代码会触发两条sql语句

select ... from `order` ...;
update `order` set .... where id = ? and version = ?;

但是出乎我意料的是,我看到了第三条sql语句

select version from `order` where id = ?;

于是乎我在find时,去除了Optimistic的配置, 发现这第三条sql就不会触发。我百思不得其解,这Optimistic如同鸡肋一样。无意中找到在stack overflow上找到一篇帖子,看到其中一段对话,豁然开朗(链接)在文章中,有一段话是重点,我在这里黏贴出来

Normally you would never use the lock() API for optimistic locking. JPA will automatically check any version columns on any update or delete.

The only purpose of the lock() API for optimistic locking is when your update depends on another object that is not changed/updated. This allows your transaction to still fail if the other object changes.

When to do this depends on the application and the use case. OPTIMISTIC will ensure the other object has not been updated at the time of your commit. OPTIMISTIC_FORCE_INCREMENT will ensure the other object has not been updated, and will increment its version on commit.

Optimistic locking is always verified on commit, and there is no guarantee of success until commit. You can use flush() to force the database locks ahead of time, or trigger an earlier error.

上面提到了Lock Mode Type 的 Optimistic类型的使用场景, 用中国话来说就是,在一个事务中,A对象的改变依赖于B对象,如果B对象在A的事物期间内发生了改变,那么A所在的事物将失败并且回滚。这种情况下就可以使用entityManager.lock(B,OPTIMISTIC)对B对象使用乐观锁。这一句话的作用在于,它会在A对象更改后,但事物提交前,查询一次B对象的version并做比对,如果比对成功A的事物保存成功,否则A事物失败,回滚。
继续看另一端代码

    @PersistenceContextEntityManager entityManager;@Transactional()public Order test(String str) {Order order67 = entityManager.find(Order.class, 67L);order67.setCancelReason(str);Order order66 = entityManager.find(Order.class, 66L);entityManager.lock(order66, LockModeType.OPTIMISTIC);return order67;}

这段代码中,查询出了order67和order66, order67在其事务期间,如果order66发生变化,order67的事物将失败。代码中entityManager.lock(order66, LockModeType.OPTIMISTIC);这一句给order66加了乐观锁,所以事物提交之前,会先check order66的version,来判断order66是否被修改。为了配合实验,在数据库执行如下代码(假定order66的version值为12)

start transaction;select * from `order` where id = 67 for update;
update `order` set version = 13 where id = 66;

开启事物, 使用排它锁将order67锁住,再更新order66对应的version,暂时不要提交。然后通过API调用刚才的test函数,此时test函数会阻塞,因为order67刚才被排它锁锁住,test函数事务提交时,会阻塞在order67的更新操作上。此时回到mysql 命令行 commit刚才的事务。按照最初的设想,在order67事务结束前,order66的version发生了变化,order67应当提交失败,函数应该会抛出ObjectOptimisticLockingFailureException,但是我却意外地发现函数执行成功了,触发了四条sql语句

select order67
select order66
update order67
select version from order67

这与我们之前讲的明显不一样,后来我突然意识到mysql的默认事务隔离级别是repeatable-read,那么就算其他事务提交,原有已开启的事务,是读不到变化的值。所以在这里我重新修改了函数,代码如下

    @PersistenceContextEntityManager entityManager;@Transactional(isolation = Isolation.READ_COMMITTED)public Order t3(String str) {Order order67 = entityManager.find(Order.class, 67L);order67.setCancelReason(str);Order order66 = entityManager.find(Order.class, 66L);entityManager.lock(order66, LockModeType.OPTIMISTIC);return order67;}

这段代码在@Transactional指定了isolation为READ_COMMITTED,此时再重复刚才的试验,程序如预期的一样抛出了ObjectOptimisticLockingFailureException。

关于Locking read(lock in share mode 和 for update),可以上mysql官网查看,里面讲的很清楚(友情链接)。

意外发现了一篇和我这篇比较相像的文章,链接在这里,我大致浏览了下,基本是对的,作者最后貌似忘了将数据库隔离级别更改为read-committed。
我又陆续看了作者的几篇文章讲的真好,这篇讲的是OPTIMISTIC-INCREMENT的使用场景,非常之贴切。
极力推荐这位大牛的博客,有事没事可以去转转

Lock Mode Type 之 Optimistic 使用场景相关推荐

  1. 动态性能视图v$lock访问很慢的解决办法

    现象如下:我在OA生产库上执行如下语句,要3分钟左右猜出结果,但是我在其他生产库上执行结果几乎是秒出,会是什么原因?  我担心的原因,如果哪天出现session阻塞,那我岂不是要完蛋,阻塞sessio ...

  2. String数据类型的应用场景

    1. 简介 string 类型是 Redis 中最基本的数据类型,最常用的数据类型,甚至被很多玩家当成 redis 唯一的数据类型去使用.string 类型在 redis 中是二进制安全(binary ...

  3. 事务的四大特性、事务处理开始与结束、v$transactio、 v$LOCK

    数据库事务的四大特性:ACID 事务的4大特性(ACID): 原子性(Atomicity):事务是数据库的逻辑工作单位,它对数据库的修改要么全部执行,要么全部不执行.一致性(Consistemcy): ...

  4. MySQL读锁的区别和应用场景分析

    读锁的概念和区别 如果在MySQL的事务里查询数据,然后在同一事务中插入或更新相关数据,常规的SELECT语句不能提供足够的保护.其他并行的事务可以更新或删除第一个事务里刚查询的相同行.InnoDB支 ...

  5. Redis在项目中的地位及使用场景剖析

    Redis在项目中的地位及使用场景剖析 一. redis 特点 所有数据存储在内存中,高速读写 提供丰富多样的数据类型:string. hash. set. sorted set.bitmap.hyp ...

  6. linux I/O-记录锁(record lock)

    记录锁(record lock)也称字节范围锁.文件范围锁.文件段锁,是一种在文件的某个字节.某个区域进行加锁的机制,记录锁总是和进程.文件相关.本篇博客介绍的是建议性记录锁. 1 记录锁的函数原型: ...

  7. Redis 数据类型及应用场景

    一. redis 特点 所有数据存储在内存中,高速读写 提供丰富多样的数据类型:string. hash. set. sorted set.bitmap.hyperloglog 提供了 AOF 和 R ...

  8. oracle授权v$lock,访问V$LOCK视图Oracle 11g出现性能问题

    最近发现Oracle 11g有个问题,拿出来和大家讨论.是在Oracle 11.2.0.3 For Linux X64环境中.检查数据库是否存在锁信息,在查询V 最近发现Oracle11g有个问题,拿 ...

  9. 基于v$lock.block及request及dba_waiters或dba_blockers学习lock锁系列七

    结论 1,v$lock.block=1表明为持锁会话,而block=0,表示为被阻塞会话(当然前提是至少有2个会话,如果仅一个会话,block=0),也就是说至少要2个会话才有意义比较block的值 ...

最新文章

  1. 2021年我的互联网秋招算法岗总结!
  2. 在 Java Web 项目中,Service 层和 Dao 层真的有必要每个类都加上接口吗
  3. 人脑计划:大脑研究如何对超级计算提出新要求
  4. ITIL应用系列之服务台
  5. iOS项目工程及目录结构
  6. nameof() 到底是编译时还是运行时行为?
  7. 邮箱smtpserver及port收集
  8. Linux——k8s命令别名修改
  9. isNaN与parseInt/parseFloat
  10. Android 系统(186)---最易懂的Android屏幕适配解决方案--总结版
  11. 关于 HDFS Append
  12. 洛谷P3369-----普通平衡树
  13. 《通关!游戏设计之道》学习笔记
  14. 190825 reverse-ogeek初赛
  15. 微信小程序引用外部文件找不到文件报错问题
  16. elasticsearch 数据类型
  17. 3 JWT 和 JWS
  18. 2004古墓丽影黄金关卡——Lara在电影中:一号门
  19. python可视化---阶梯图step()
  20. mel表达式_表达式和 MEL 语法之间的区别

热门文章

  1. 前端知识总结之linux
  2. C++中野指针和空指针和无类型指针
  3. Python文件自我复制感染病毒及其查杀解毒
  4. 暴打JavaScript语法之getElementById()、getElementsByTagName()全能解释
  5. 圆台下料展开计算方法_圆台展开的方法
  6. MySql高级(二)
  7. 前端HTML小米官方网站界面部分实现
  8. 磁感应强度B与磁场强度H的区别,联系与物理意义
  9. 乌拉姆距离(Ulam)
  10. 网站搭建(简洁版本)