休眠CascadeType.LOCK陷阱
介绍
引入了Hibernate 显式锁定支持以及Cascade Types之后 ,就该分析CascadeType.LOCK行为了。
休眠锁定请求触发内部LockEvent 。 关联的DefaultLockEventListener可以将锁定请求级联到锁定实体子级。
由于CascadeType.ALL也包括CascadeType.LOCK ,因此当锁定请求从父级实体传播到子级实体时,值得理解。
测试时间
我们将从以下实体模型开始:
Post是PostDetail一对一关联和Comment一对多关联的Parent实体,这些关联用CascadeType.ALL标记:
@OneToMany(cascade = CascadeType.ALL, mappedBy = "post", orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();@OneToOne(cascade = CascadeType.ALL, mappedBy = "post", optional = false, fetch = FetchType.LAZY)
private PostDetails details;
所有即将到来的测试用例将使用以下实体模型图:
doInTransaction(session -> {Post post = new Post();post.setName("Hibernate Master Class");post.addDetails(new PostDetails());post.addComment(new Comment("Good post!"));post.addComment(new Comment("Nice post!"));session.persist(post);
});
锁定管理实体
将受管实体加载到当前正在运行的持久性上下文中,并将所有实体状态更改转换为DML语句。
当托管父实体被锁定时:
doInTransaction(session -> {Post post = (Post) session.createQuery("select p " +"from Post p " +"join fetch p.details " +"where " +" p.id = :id").setParameter("id", 1L).uniqueResult();session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)).lock(post);
});
只有父实体被锁定,因此可以防止级联:
select id from Post where id = 1 for update
Hibernate定义了一个范围 LockOption ,该范围 (根据JavaDocs)应允许将锁定请求传播到Child实体:
“范围”是JPA定义的术语。 基本上,这是关联锁定的级联。
session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE))
.setScope(true)
.lock(post);
设置范围标志不会改变任何东西,只有被管理实体被锁定:
select id from Post where id = 1 for update
锁定独立实体
除了实体锁定之外,锁定请求还可以重新关联分离的实体。 为了证明这一点,我们将在锁定实体请求之前和之后检查Post实体图:
void containsPost(Session session, Post post, boolean expected) {assertEquals(expected, session.contains(post));assertEquals(expected, session.contains(post.getDetails()));for(Comment comment : post.getComments()) {assertEquals(expected, session.contains(comment));}
}
以下测试演示了CascadeType.LOCK如何用于分离的实体:
//Load the Post entity, which will become detached
Post post = doInTransaction(session -> (Post) session.createQuery("select p " +"from Post p " +"join fetch p.details " +"join fetch p.comments " +"where " +" p.id = :id")
.setParameter("id", 1L)
.uniqueResult());//Change the detached entity state
post.setName("Hibernate Training");
doInTransaction(session -> {//The Post entity graph is detachedcontainsPost(session, post, false);//The Lock request associates //the entity graph and locks the requested entitysession.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)).lock(post);//Hibernate doesn't know if the entity is dirtyassertEquals("Hibernate Training", post.getName());//The Post entity graph is attachedcontainsPost(session, post, true);
});
doInTransaction(session -> {//The detached Post entity changes have been lostPost _post = (Post) session.get(Post.class, 1L);assertEquals("Hibernate Master Class", _post.getName());
});
锁定请求重新关联了实体图,但是当前正在运行的Hibernate Session并未意识到处于分离状态的实体变脏了。 仅在不强制执行UPDATE或选择当前数据库状态进行进一步比较的情况下,才重新连接实体。
一旦对实体进行管理, 脏检查机制将检测到任何进一步的更改,并且刷新也会传播重新附加的更改。 如果在管理实体时未发生任何更改,则不会安排该实体进行刷新。
如果要确保分离的实体状态始终与数据库同步,则需要使用merge或update 。
当scope选项设置为true时,分离的实体传播lock选项:
session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE))
.setScope(true)
.lock(post);
Post实体锁定事件会传播到所有Child实体(因为我们正在使用CascadeType.ALL ):
select id from Comment where id = 1 for update
select id from Comment where id = 2 for update
select id from PostDetails where id = 1 for update
select id from Post where id = 1 for update
结论
锁级联不是简单明了或直观的。 显式锁定需要勤奋(我们获取的锁越多,死锁的机会就越大),并且无论如何,最好保留对Child实体锁传播的完全控制权。 因此,与并发编程最佳实践类似,手动锁定优于自动锁定传播。
- 代码可在GitHub上获得 。
翻译自: https://www.javacodegeeks.com/2015/03/hibernate-cascadetype-lock-gotchas.html
休眠CascadeType.LOCK陷阱相关推荐
- lock.lock_HibernateCascadeType.LOCK陷阱
lock.lock 介绍 引入了Hibernate 显式锁定支持以及Cascade Types之后 ,就该分析CascadeType.LOCK行为了. Hibernate锁定请求触发内部LockEve ...
- JPA 和休眠级联类型
JPA 允许您将状态转换从父实体传播到子实体.为此,JPAjavax.persistence.CascadeType定义了各种级联类型: 全部 - 级联所有实体状态转换 持久 - 级联实体持久化操作. ...
- centos8 合上笔记本盖子不休眠,不断网
修改文件 /etc/systemd/logind.conf 用centos 的主机的時候, 用 vim 时出现 -bash: vim: command not found. 只能使用 vi. 那么如何 ...
- JPA和Hibernate级联类型的初学者指南
介绍 JPA将实体状态转换转换为数据库DML语句. 由于对实体图进行操作很常见,因此JPA允许我们将实体状态更改从父级传播到子级 . 通过CascadeType映射配置此行为. JPA与Hiberna ...
- jpa和hibernate_JPA和Hibernate级联类型的初学者指南
jpa和hibernate 介绍 JPA将实体状态转换转换为数据库DML语句. 由于对实体图进行操作很常见,因此JPA允许我们将实体状态更改从父级传播到子级 . 通过CascadeType映射配置此行 ...
- c++高级编程(第4版).pdf_《C++并发编程实战第2版》第四章:同步并发操作(1/4)
本章主要内容 等待一个事件 用期望等待一次性事件 带时间限制的等待 使用操作的同步来简化代码 上一章中,我们看到各种在线程间保护共享数据的方法.但有时,你不仅需要保护数据,还需要同步不同线程上的操作. ...
- java 总结几种线程异步转同步的方法
转载自https://blog.csdn.net/Veson__/article/details/53898890 在做一款app的时候,用到了一个异步执行的api,而我想要的是同步执行,查了一些资料 ...
- 《C++ Concurrency in Action》笔记
<C++ Concurrency in Action>笔记 1 你好,C++的并发世界 1.1 何谓并发 1.1.1 计算机系统中的并发 1.1.2 并发的途径 多进程并发 多线程并发 1 ...
- Java与C语言中的锁
Java与C语言中的锁 C 嵌入式汇编的语法格式是: asm(code : output operand list : input operand list : clobber list) __asm ...
最新文章
- 智能车竞赛,AI视觉组赛题浅析
- 帧布局(FrameLayout)及属性
- 光纤收发器的分类介绍
- Linux中如何打开trn文件,如何通过trn日志文件恢复SQL Server
- 机器学习难?那是你没看过这张路线图!
- [] __nw_connection
- verilog中generate语句的使用
- Java多个pdf文件合并一个pdf(多页)
- Word删除与新增页眉或页脚的横线
- 连续分配管理方式(单一连续分配 固定分区分配 动态分区分配)
- Mono.Cecil使用示例之获取源文件路径
- C语言:鸡兔同笼(随机输入头数和脚数)
- 计算机语言中daly什么意思,计算机存储器 - dalyHu的个人空间 - OSCHINA - 中文开源技术交流社区...
- Stochastic Weight Averaging
- php数字转换百千万,PHP数字金额转换成中文大写显示
- 2022年危险化学品经营单位主要负责人及危险化学品经营单位主要负责人操作证考试
- JDK9 的字符串底层原理是什么?
- 缓存Cache概述——缓存Cache1.1.1
- Power BI(一)Power Query简介
- 开机读不了bios,提示为 press del to enter setup, esc to enter boot me
热门文章
- java中判断数组中元素出现的次数
- 多线程三种同步方式(模拟银行取款)
- linux微信公众号报警,zabbix报警媒介,微信报警,邮件报警
- html5中 save方法,如何HTML5画布另存为窗口8 Metro应用中的图像文件?(How to save html5 c...
- vue 字典_【开源】基于Vue的前端组件库HeyUI
- linux wait函数头文件_手把手教Linux驱动9-等待队列waitq
- JavaWeb项目:简易小米商城系统
- 郎溪 溪流_到无限(溪流)和超越!
- spock测试_将Spock 1.3测试迁移到Spock 2.0
- tdd 单元测试_何时给定在单元测试和TDD中的重要性