Hibernate锁定模式– PESSIMISTIC_FORCE_INCREMENT锁定模式如何工作

介绍

在上 一篇 文章中 ,我介绍了OPTIMISTIC_FORCE_INCREMENT锁定模式,并将其应用于将子实体版本更改传播到锁定的父实体。 在本文中,我将介绍PESSIMISTIC_FORCE_INCREMENT锁定模式,并将其与乐观的锁定模式进行比较。

相像多于不同

正如我们已经发现的那样,即使当前事务没有修改锁定的实体状态, OPTIMISTIC_FORCE_INCREMENT锁定模式也可以增加实体的版本。 对于每种锁定模式,Hibernate定义了一个关联的LockingStrategy ,并且OPTIMISTIC_FORCE_INCREMENT锁定模式事件由OptimisticForceIncrementLockingStrategy处理:

public class OptimisticForceIncrementLockingStrategy implements LockingStrategy {//code omitted for brevity@Overridepublic void lock(Serializable id, Object version, Object object, int timeout, SessionImplementor session) {if ( !lockable.isVersioned() ) {throw new HibernateException( "[" + lockMode + "] not supported for non-versioned entities [" + lockable.getEntityName() + "]" );}final EntityEntry entry = session.getPersistenceContext().getEntry( object );// Register the EntityIncrementVersionProcess action to run just prior to transaction commit.( (EventSource) session ).getActionQueue().registerProcess( new EntityIncrementVersionProcess( object, entry ) );}
}

该策略在当前的持久性上下文操作队列中注册一个EntityIncrementVersionProcess 。 在完成当前正在运行的事务之前,锁定的实体版本会增加。

public class EntityIncrementVersionProcess implements BeforeTransactionCompletionProcess {//code omitted for brevity@Overridepublic void doBeforeTransactionCompletion(SessionImplementor session) {final EntityPersister persister = entry.getPersister();final Object nextVersion = persister.forceVersionIncrement( entry.getId(), entry.getVersion(), session );entry.forceLocked( object, nextVersion );}
}

与OPTIMISTIC_FORCE_INCREMENT相似 , PESSIMISTIC_FORCE_INCREMENT锁定模式由PessimisticForceIncrementLockingStrategy处理:

public class PessimisticForceIncrementLockingStrategy implements LockingStrategy {//code omitted for brevity@Overridepublic void lock(Serializable id, Object version, Object object, int timeout, SessionImplementor session) {if ( !lockable.isVersioned() ) {throw new HibernateException( "[" + lockMode + "] not supported for non-versioned entities [" + lockable.getEntityName() + "]" );}final EntityEntry entry = session.getPersistenceContext().getEntry( object );final EntityPersister persister = entry.getPersister();final Object nextVersion = persister.forceVersionIncrement( entry.getId(), entry.getVersion(), session );entry.forceLocked( object, nextVersion );}
}

锁定的实体立即增加,因此这两种锁定模式执行相同的逻辑,但时间不同。 PESSIMISTIC_FORCE_INCREMENT的命名可能会使您想到您正在使用悲观的锁定策略,而实际上,此锁定模式只是一种乐观的锁定变体。

悲观锁需要显式物理锁(共享或独占),而乐观锁则依赖于当前事务隔离级别的隐式锁。

存储库用例

我将重用之前的练习,然后切换到使用PESSIMISTIC_FORCE_INCREMENT锁定模式。 回顾一下,我们的域模型包含:

  • 一个存储库实体,其版本随每次新提交而增加
  • 一个Commit实体,封装了一个原子存储库状态转换
  • 一个CommitChange组件,封装了一个存储库资源更改

防止并行修改

爱丽丝和鲍勃同时访问我们的系统。 从数据库中获取存储库实体之后,它始终处于锁定状态:

private final CountDownLatch startLatch = new CountDownLatch(1);
private final CountDownLatch endLatch = new CountDownLatch(1);@Test
public void testConcurrentPessimisticForceIncrementLockingWithLockWaiting() throws InterruptedException {LOGGER.info("Test Concurrent PESSIMISTIC_FORCE_INCREMENT Lock Mode With Lock Waiting");doInTransaction(new TransactionCallable<Void>() {@Overridepublic Void execute(Session session) {try {Repository repository = (Repository) session.get(Repository.class, 1L);session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_FORCE_INCREMENT)).lock(repository);executeNoWait(new Callable<Void>() {@Overridepublic Void call() throws Exception {return doInTransaction(new TransactionCallable<Void>() {@Overridepublic Void execute(Session _session) {LOGGER.info("Try to get the Repository row");startLatch.countDown();Repository _repository = (Repository) _session.get(Repository.class, 1L);_session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_FORCE_INCREMENT)).lock(_repository);Commit _commit = new Commit(_repository);_commit.getChanges().add(new Change("index.html", "0a1,2..."));_session.persist(_commit);_session.flush();endLatch.countDown();return null;}});}});startLatch.await();LOGGER.info("Sleep for 500ms to delay the other transaction PESSIMISTIC_FORCE_INCREMENT Lock Mode acquisition");Thread.sleep(500);Commit commit = new Commit(repository);commit.getChanges().add(new Change("README.txt", "0a1,5..."));commit.getChanges().add(new Change("web.xml", "17c17..."));session.persist(commit);return null;} catch (InterruptedException e) {fail("Unexpected failure");}return null;}});endLatch.await();
}

该测试用例生成以下输出:

#Alice selects the Repository
Query:{[select lockmodeop0_.id as id1_2_0_, lockmodeop0_.name as name2_2_0_, lockmodeop0_.version as version3_2_0_ from repository lockmodeop0_ where lockmodeop0_.id=?][1]} #Alice locks the Repository using a PESSIMISTIC_FORCE_INCREMENT Lock Mode
Query:{[update repository set version=? where id=? and version=?][1,1,0]} #Bob tries to get the Repository but the SELECT is blocked by Alice lock 
INFO  [pool-1-thread-1]: c.v.h.m.l.c.LockModePessimisticForceIncrementTest - Try to get the Repository row#Alice sleeps for 500ms to prove that Bob is waiting for her to release the acquired lock
Sleep for 500ms to delay the other transaction PESSIMISTIC_FORCE_INCREMENT Lock Mode acquisition#Alice makes two changes and inserts a new Commit<a href="https://vladmihalcea.files.wordpress.com/2015/02/explicitlockingpessimisticforceincrementfailfast.png"><img src="https://vladmihalcea.files.wordpress.com/2015/02/explicitlockingpessimisticforceincrementfailfast.png?w=585" alt="ExplicitLockingPessimisticForceIncrementFailFast" width="585" height="224" class="alignnone size-large wp-image-3955" /></a>
Query:{[insert into commit (id, repository_id) values (default, ?)][1]} 
Query:{[insert into commit_change (commit_id, diff, path) values (?, ?, ?)][1,0a1,5...,README.txt]#The Repository version is bumped up to version 1 and a conflict is raised
Query:{[insert into commit_change (commit_id, diff, path) values (?, ?, ?)][1,17c17...,web.xml]} Query:{[update repository set version=? where id=? and version=?][1,1,0]}#Alice commits the transaction, therefore releasing all locks
DEBUG [main]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection#Bob Repository SELECT can proceed 
Query:{[select lockmodepe0_.id as id1_2_0_, lockmodepe0_.name as name2_2_0_, lockmodepe0_.version as version3_2_0_ from repository lockmodepe0_ where lockmodepe0_.id=?][1]} #Bob can insert his changes
Query:{[update repository set version=? where id=? and version=?][2,1,1]} 
Query:{[insert into commit (id, repository_id) values (default, ?)][1]} 
Query:{[insert into commit_change (commit_id, diff, path) values (?, ?, ?)][2,0a1,2...,index.html]}

下图可以很容易地看到此锁定过程:

显式锁定悲观力量增加

每当修改数据库行时, HSQLDB测试数据库“ 两阶段锁定”实现都会使用过程粒度表锁定。

这就是Bob不能获得Alice刚刚更新的Repository数据库行上的读取锁的原因。 其他数据库(例如Oracle,PostgreSQL)使用MVCC ,因此允许SELECT继续执行(使用当前的修改事务撤消日志来重新创建前一个行状态),同时阻止冲突的数据修改语句(例如,当其他并发事务已经停止时更新存储库行)尚未提交锁定的实体状态更改)。

快速失败

即时版本增加具有一些有趣的好处:

  • 如果版本UPDATE成功(获取了排他行级锁),则其他任何并发事务都无法修改锁定的数据库行。 这是将逻辑锁(版本递增)升级为物理锁(数据库互斥锁)的时刻。
  • 如果版本UPDATE失败(因为其他一些并发事务已经提交了版本更改),则可以立即回滚当前正在运行的事务(而不是在提交期间等待事务失败)

后一种用例可以如下所示:

显式锁定悲观力量递增失败

对于这种情况,我们将使用以下测试用例:

@Test
public void testConcurrentPessimisticForceIncrementLockingFailFast() throws InterruptedException {LOGGER.info("Test Concurrent PESSIMISTIC_FORCE_INCREMENT Lock Mode fail fast");doInTransaction(new TransactionCallable<Void>() {@Overridepublic Void execute(Session session) {try {Repository repository = (Repository) session.get(Repository.class, 1L);executeAndWait(new Callable<Void>() {@Overridepublic Void call() throws Exception {return doInTransaction(new TransactionCallable<Void>() {@Overridepublic Void execute(Session _session) {Repository _repository = (Repository) _session.get(Repository.class, 1L);_session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_FORCE_INCREMENT)).lock(_repository);Commit _commit = new Commit(_repository);_commit.getChanges().add(new Change("index.html", "0a1,2..."));_session.persist(_commit);_session.flush();return null;}});}});session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_FORCE_INCREMENT)).lock(repository);fail("Should have thrown StaleObjectStateException!");} catch (StaleObjectStateException expected) {LOGGER.info("Failure: ", expected);}return null;}});
}

生成以下输出:

#Alice selects the Repository
Query:{[select lockmodeop0_.id as id1_2_0_, lockmodeop0_.name as name2_2_0_, lockmodeop0_.version as version3_2_0_ from repository lockmodeop0_ where lockmodeop0_.id=?][1]} #Bob selects the Repository too
Query:{[select lockmodepe0_.id as id1_2_0_, lockmodepe0_.name as name2_2_0_, lockmodepe0_.version as version3_2_0_ from repository lockmodepe0_ where lockmodepe0_.id=?][1]} #Bob locks the Repository using a PESSIMISTIC_FORCE_INCREMENT Lock Mode
Query:{[update repository set version=? where id=? and version=?][1,1,0]} #Bob makes a change and inserts a new Commit
Query:{[insert into commit (id, repository_id) values (default, ?)][1]} 
Query:{[insert into commit_change (commit_id, diff, path) values (?, ?, ?)][1,0a1,2...,index.html]} #Bob commits the transaction
DEBUG [pool-3-thread-1]: o.h.e.t.i.j.JdbcTransaction - committed JDBC Connection#Alice tries to lock the Repository
Query:{[update repository set version=? where id=? and version=?][1,1,0]} #Alice cannot lock the Repository, because the version has changed
INFO  [main]: c.v.h.m.l.c.LockModePessimisticForceIncrementTest - Failure: 
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.vladmihalcea.hibernate.masterclass.laboratory.concurrency.LockModePessimisticForceIncrementTest$Repository#1]

结论

与OPTIMISTIC_FORCE_INCREMENT一样, PESSIMISTIC_FORCE_INCREMENT锁定模式对于将实体状态更改传播到父实体非常有用。

尽管锁定机制相似,但是PESSIMISTIC_FORCE_INCREMENT可以当场应用,从而允许当前正在运行的事务即时评估锁定结果。

  • 代码可在GitHub上获得 。

翻译自: https://www.javacodegeeks.com/2015/02/hibernate-locking-patterns-pessimistic_force_increment-lock-mode-work.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/338271.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言中字符串的结束标志是什么

C语言中字符串的结束标志是【\0】。C语言中没有专门的字符串变量&#xff0c;通常用一个字符数组来存放一个字符串&#xff0c;字符串总是以【\0】作为结束符。\0就是8位的00000000&#xff0c;因为字符类型中并没有对应的这个字符&#xff0c;所以这么写。\0就是 字符串结束标…

php 5.6.27 在某些机器上正常,在 Windows 10 64、PHP 5.6 下重命名中文名文件,提示错误的解决...

1、重命名某个目录中的文件名&#xff0c;其代码&#xff0c;如图1图12、报错&#xff1a;rename(E:/wwwroot/avatar/BEIJI/侯森.jpg,E:/wwwroot/avatar/BEIJI/378477.jpg): ϵͳ&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ָ&#xfffd;&#xfffd;&#xfffd;&am…

c语言中字符常量是什么?

c语言中字符常量是什么&#xff1f;字符常量&#xff1a;一个用单引号括起来的单个字符&#xff08;或字符转义序列或三字母词&#xff09;实质&#xff08;含义&#xff09;&#xff1a;是一个整形值。属于四大基本数据类型&#xff08;分别是整型&#xff0c;浮点型&#xff…

Hibernate锁定模式– OPTIMISTIC_FORCE_INCREMENT锁定模式如何工作

介绍 在我以前的文章中 &#xff0c;我解释了OPTIMISTIC锁定模式是如何工作的&#xff0c;以及它如何帮助我们同步外部实体状态更改。 在本文中&#xff0c;我们将介绍OPTIMISTIC_FORCE_INCREMENT锁定模式的使用模式。 使用LockModeType.OPTIMISTIC &#xff0c;将在当前正在运…

c语言的输入输出语句有哪些?

c语言的输入输出语句有&#xff1a;“getchar(void);”和“putchar(int c);”、“scanf("格式控制字符串",地址列表);”和“printf("格式控制字符串",输出列表);”、“gets()”和“puts()”等等。一&#xff1a;控制台输入输出(1)字符数据的输入/输出字符输…

primefaces_PrimeFaces:在动态生成的对话框中打开外部页面

primefaces我已经在即将出版的PrimeFaces Cookbook版本2中写过一篇食谱的博客。 在这篇文章中&#xff0c;我想发表第二篇关于一个名为Dialog Framework的小型框架的文章。 我个人喜欢它&#xff0c;因为我记得我为使用Struts框架付出同样的代价。 当您想将外部页面加载到弹出窗…

c语言源文件经过编译后生成文件的后缀是什么?

c语言源文件经过编译后&#xff0c;生成文件的后缀是“.obj”。C语言源文件后缀名是“.c”&#xff0c;编译生成的文件后缀名是“.obj”&#xff0c;连接后可执行文件的后缀名是“.exe”。C语言创建程序的步骤&#xff1a;编辑&#xff1a;就是创建和修改C程序的源代码-我们编写…

java编译器jdk版本_以编程方式确定Java类的JDK编译版本

java编译器jdk版本当需要确定使用哪个JDK版本来编译特定的Java .class文件时&#xff0c; 通常使用的方法是使用javap并在javap输出中查找列出的“主要版本”。 我在我的博客文章Autoboxing&#xff0c;Unboxing和NoSuchMethodError中引用了这种方法&#xff0c;但是在继续以编…

c语言指针用法有哪些

c语言指针用法&#xff1a;一&#xff0c;指针定义&#xff1a;指针变量的取值范围取值0~4G,是一种数据类型&#xff08;无符号整数&#xff0c;代表了内存编号&#xff09;。它可以用来定义变量&#xff08;与int、long一样&#xff09;&#xff0c;与int、long不同的它存储整…

ogm session_带有Hibernate OGM的NoSQL –第一部分:持久化您的第一个实体

ogm sessionHibernate OGM的第一个最终版本已经发布 &#xff0c;团队从发布狂潮中恢复了一些。 因此&#xff0c;他们考虑开设一系列教程风格的博客&#xff0c;使您有机会轻松地从Hibernate OGM重新开始。 感谢Gunnar Morling&#xff08; gunnarmorling &#xff09;创建了本…

c语言volatile关键字的作用是什么?

一.前言1.编译器优化介绍&#xff1a;由于内存访问速度远不及CPU处理速度&#xff0c;为提高机器整体性能&#xff0c;在硬件上引入硬件高速缓存Cache&#xff0c;加速对内存的访问。另外在现代CPU中指令的执行并不一定严格按照顺序执行&#xff0c;没有相关性的指令可以乱序执…

jaxb解析字符串xml_一个JAXB Nuance:字符串与枚举(受限制的XSD字符串)的枚举

jaxb解析字符串xml尽管用于XML绑定的Java体系结构 &#xff08; JAXB &#xff09;在名义情况下&#xff08;尤其是自Java SE 6以来&#xff09; 相当容易使用&#xff0c;但它也存在许多细微差别。 一些常见的细微差别是由于无法将 XML架构定义 &#xff08;XSD&#xff09;类…

php伪静态后不能访问html,php伪静态后html不能访问怎么办

php伪静态后html不能访问的解决办法&#xff1a;首先判断文件是否存在&#xff1b;然后设置存在则不rewirte&#xff0c;不存在且符合规则才rewrite&#xff1b;最后修改htaccess文件即可。推荐&#xff1a;《PHP视频教程》具体问题&#xff1a;PHP伪静态后不能访问纯html文件.…

c语言中,char型数据是以什么形式存储的?

C语言 字符型&#xff08;char&#xff09;简介字符型&#xff08;char&#xff09;用于储存字符&#xff08;character&#xff09;&#xff0c;如英文字母或标点。严格来说&#xff0c;char 其实也是整数类型&#xff08;integer type&#xff09;&#xff0c;因为char 类型储…

声明式编程与函数式编程_实用程序类与函数式编程无关

声明式编程与函数式编程最近&#xff0c;我被指控反对函数式编程&#xff0c;因为我将实用程序类称为反模式 。 绝对是错的&#xff01; 好吧&#xff0c;我确实认为它们是一个糟糕的反模式&#xff0c;但是它们与函数式编程无关。 我相信有两个基本原因。 首先&#xff0c;函数…

C语言中位运算符有哪些

C语言中位运算符有&#xff1a;位操作是程序设计中对位模式按位或二进制数的一元和二元操作。在许多古老的微处理器上&#xff0c; 位运算比加减运算略快&#xff0c; 通常位运算比乘除法运算要快很多。在现代架构中&#xff0c; 情况并非如此&#xff1a;位运算的运算速度通常…

jsf表单验证_JSF:在正确的阶段进行验证(了解生命周期)

jsf表单验证嗨&#xff0c;大家好&#xff01; 尽管标题强调验证一词&#xff0c;但本文实际上是关于JSF生命周期的。 那是因为我相信&#xff0c;真正了解生命周期的最简单方法之一就是通过做出我们一直在做的事情&#xff1a;验证用户输入。 通常&#xff0c;了解所谓的JSF…

java广度优先爬虫示例,【爬虫】广度优先遍历抓取数据概述

这次都是一些纯语言的表达&#xff0c;可能会有点啰嗦&#xff0c;或者有点枯燥&#xff0c;也是对爬虫的一些小小的见解&#xff0c;可能只是一些常见话&#xff0c;哈哈&#xff0c;还是耐心的写完。网络爬虫的整体执行流程&#xff1a;1)确定一个(多个)种子网页2)进行数据内…

if语句的用法是什么

if语句的用法&#xff1a;if语句是指编程语言&#xff08;包括c语言、C#、VB、java、汇编语言等&#xff09;中用来判定所给定的条件是否满足&#xff0c;根据判定的结果&#xff08;真或假&#xff09;决定执行给出的两种操作之一。if语句概述if语句是指编程语言&#xff08;包…

c语言如何实现玫瑰花

c语言实现玫瑰花的方法&#xff1a;#include #include ?#include #include #include #pragma comment(lib,"winmm.lib")//定义全局变量int rosesize 500;int h -250;//定义结构体struct DOT {double x;double y;double z;double r;double g;};bool calc(double a,…