hibernate乐观锁

介绍

Hibernate提供了一种乐观的锁定机制 ,即使长时间通话也可以防止更新丢失 。 结合实体存储,跨越多个用户请求(扩展的持久性上下文或分离的实体),Hibernate可以保证应用程序级的可重复读取 。

脏检查机制检测实体状态更改并增加实体版本。 尽管始终考虑基本属性更改,但是Hibernate集合在这方面更加微妙。

拥有与反向收藏

在关系数据库中,两个记录通过外键引用关联。 在这种关系中,引用记录是父记录,而引用行(外键侧)是子记录。 非空外键只能引用现有的父记录。

在面向对象的空间中,可以在两个方向上表示这种关联。 我们可以从孩子到父母有一对多的引用,而父母也可以有一对多的孩子集合。

因为双方都有可能控制数据库外键状态,所以我们必须确保只有一方是此关联的所有者。 仅拥有方状态更改会传播到数据库。 非持有端历来称为侧。

接下来,我将描述对该关联进行建模的最常用方法。

单向父项拥有子项关联映射

只有父方具有@OneToMany非逆子级集合。 子实体根本不引用父实体。

@Entity(name = "post")
public class Post {...@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)private List<Comment> comments = new ArrayList<Comment>();...
}

单向父-子-子-子组件关联映射映射

子端不一定总是必须是实体,我们可以将其建模为组件类型 。 一个Embeddable对象(组件类型)可能同时包含基本类型和关联映射,但永远不能包含@Id。 可嵌入对象及其拥有的实体将被持久保存/删除。

父级具有@ElementCollection子级关联。 子实体只能通过不可查询的特定于 Hibernate的@Parent批注来引用父实体。

@Entity(name = "post")
public class Post {...@ElementCollection@JoinTable(name = "post_comments", joinColumns = @JoinColumn(name = "post_id"))@OrderColumn(name = "comment_index")private List<Comment> comments = new ArrayList<Comment>();...public void addComment(Comment comment) {comment.setPost(this);comments.add(comment);}
}   @Embeddable
public class Comment {...@Parentprivate Post post;...
}

双向父子侧子关联映射

父级是拥有方,因此它有一个@OneToMany非逆(不包含mappingBy指令)子级集合。 子实体通过@ManyToOne关联引用父实体,该关联既不可插入也不可更新:

@Entity(name = "post")
public class Post {...@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)private List<Comment> comments = new ArrayList<Comment>();...public void addComment(Comment comment) {comment.setPost(this);comments.add(comment);}
}   @Entity(name = "comment")
public class Comment {...@ManyToOne@JoinColumn(name = "post_id", insertable = false, updatable = false)private Post post;...
}

双向儿童拥有侧-父母关联映射

子实体通过引用父实体@ManyToOne协会和家长有一个的mappedBy @OneToMany孩子集合。 父侧是反侧,因此仅@ManyToOne状态更改会传播到数据库。

即使只有一个拥有的一方,通过使用add / removeChild()方法使双方保持同步始终是一个好习惯。

@Entity(name = "post")
public class Post {...@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "post")private List<Comment> comments = new ArrayList<Comment>();...public void addComment(Comment comment) {comment.setPost(this);comments.add(comment);}
}   @Entity(name = "comment")
public class Comment {...@ManyToOneprivate Post post;   ...
}

单向儿童拥有侧父母关系映射

子实体通过@ManyToOne关联引用父实体。 父级没有@OneToMany子级集合,因此子级实体成为所有者。 此关联映射类似于关系数据外键链接。

@Entity(name = "comment")
public class Comment {...@ManyToOneprivate Post post;   ...
}

集合版本控制

JPA 2.1规范的3.4.2部分将乐观锁定定义为:

将对象写入数据库时​​,持久性提供程序运行时会更新version属性。 版本检查中包括所有非关系字段和适当的关系以及实体所拥有的所有关系[35]。

[35]这包括在联接表中维护的拥有的关系

注意:只有拥有方的子级集合可以更新父级版本。

测试时间

让我们测试一下父子关联类型如何影响父版本。 因为我们对子级集合的脏检查感兴趣,所以将跳过单向的子级拥有方-父级关联,因为在这种情况下,父级不包含子级集合。

测试用例

以下测试用例将用于所有集合类型用例:

protected void simulateConcurrentTransactions(final boolean shouldIncrementParentVersion) {final ExecutorService executorService = Executors.newSingleThreadExecutor();doInTransaction(new TransactionCallable<Void>() {@Overridepublic Void execute(Session session) {try {P post = postClass.newInstance();post.setId(1L);post.setName("Hibernate training");session.persist(post);return null;} catch (Exception e) {throw new IllegalArgumentException(e);}}});doInTransaction(new TransactionCallable<Void>() {@Overridepublic Void execute(final Session session) {final P post = (P) session.get(postClass, 1L);try {executorService.submit(new Callable<Void>() {@Overridepublic Void call() throws Exception {return doInTransaction(new TransactionCallable<Void>() {@Overridepublic Void execute(Session _session) {try {P otherThreadPost = (P) _session.get(postClass, 1L);int loadTimeVersion = otherThreadPost.getVersion();assertNotSame(post, otherThreadPost);assertEquals(0L, otherThreadPost.getVersion());C comment = commentClass.newInstance();comment.setReview("Good post!");otherThreadPost.addComment(comment);_session.flush();if (shouldIncrementParentVersion) {assertEquals(otherThreadPost.getVersion(), loadTimeVersion + 1);} else {assertEquals(otherThreadPost.getVersion(), loadTimeVersion);}return null;} catch (Exception e) {throw new IllegalArgumentException(e);}}});}}).get();} catch (Exception e) {throw new IllegalArgumentException(e);}post.setName("Hibernate Master Class");session.flush();return null;}});
}

单向父母所有子女的关联测试

#create tables
Query:{[create table comment (id bigint generated by default as identity (start with 1), review varchar(255), primary key (id))][]}
Query:{[create table post (id bigint not null, name varchar(255), version integer not null, primary key (id))][]}
Query:{[create table post_comment (post_id bigint not null, comments_id bigint not null, comment_index integer not null, primary key (post_id, comment_index))][]}
Query:{[alter table post_comment add constraint UK_se9l149iyyao6va95afioxsrl  unique (comments_id)][]}
Query:{[alter table post_comment add constraint FK_se9l149iyyao6va95afioxsrl foreign key (comments_id) references comment][]}
Query:{[alter table post_comment add constraint FK_6o1igdm04v78cwqre59or1yj1 foreign key (post_id) references post][]} #insert post in primary transaction
Query:{[insert into post (name, version, id) values (?, ?, ?)][Hibernate training,0,1]} #select post in secondary transaction
Query:{[select entityopti0_.id as id1_1_0_, entityopti0_.name as name2_1_0_, entityopti0_.version as version3_1_0_ from post entityopti0_ where entityopti0_.id=?][1]} #insert comment in secondary transaction
#optimistic locking post version update in secondary transaction
Query:{[insert into comment (id, review) values (default, ?)][Good post!]}
Query:{[update post set name=?, version=? where id=? and version=?][Hibernate training,1,1,0]}
Query:{[insert into post_comment (post_id, comment_index, comments_id) values (?, ?, ?)][1,0,1]} #optimistic locking exception in primary transaction
Query:{[update post set name=?, version=? where id=? and version=?][Hibernate Master Class,1,1,0]}
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.vladmihalcea.hibernate.masterclass.laboratory.concurrency.EntityOptimisticLockingOnUnidirectionalCollectionTest$Post#1]

单向父-子-子组件关联测试

#create tables
Query:{[create table post (id bigint not null, name varchar(255), version integer not null, primary key (id))][]}
Query:{[create table post_comments (post_id bigint not null, review varchar(255), comment_index integer not null, primary key (post_id, comment_index))][]}
Query:{[alter table post_comments add constraint FK_gh9apqeduab8cs0ohcq1dgukp foreign key (post_id) references post][]} #insert post in primary transaction
Query:{[insert into post (name, version, id) values (?, ?, ?)][Hibernate training,0,1]} #select post in secondary transaction
Query:{[select entityopti0_.id as id1_0_0_, entityopti0_.name as name2_0_0_, entityopti0_.version as version3_0_0_ from post entityopti0_ where entityopti0_.id=?][1]}
Query:{[select comments0_.post_id as post_id1_0_0_, comments0_.review as review2_1_0_, comments0_.comment_index as comment_3_0_ from post_comments comments0_ where comments0_.post_id=?][1]} #insert comment in secondary transaction
#optimistic locking post version update in secondary transaction
Query:{[update post set name=?, version=? where id=? and version=?][Hibernate training,1,1,0]}
Query:{[insert into post_comments (post_id, comment_index, review) values (?, ?, ?)][1,0,Good post!]} #optimistic locking exception in primary transaction
Query:{[update post set name=?, version=? where id=? and version=?][Hibernate Master Class,1,1,0]}
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.vladmihalcea.hibernate.masterclass.laboratory.concurrency.EntityOptimisticLockingOnComponentCollectionTest$Post#1]

双向父母拥有-子-孩子关联测试

#create tables
Query:{[create table comment (id bigint generated by default as identity (start with 1), review varchar(255), post_id bigint, primary key (id))][]}
Query:{[create table post (id bigint not null, name varchar(255), version integer not null, primary key (id))][]}
Query:{[create table post_comment (post_id bigint not null, comments_id bigint not null)][]}
Query:{[alter table post_comment add constraint UK_se9l149iyyao6va95afioxsrl  unique (comments_id)][]}
Query:{[alter table comment add constraint FK_f1sl0xkd2lucs7bve3ktt3tu5 foreign key (post_id) references post][]}
Query:{[alter table post_comment add constraint FK_se9l149iyyao6va95afioxsrl foreign key (comments_id) references comment][]}
Query:{[alter table post_comment add constraint FK_6o1igdm04v78cwqre59or1yj1 foreign key (post_id) references post][]} #insert post in primary transaction
Query:{[insert into post (name, version, id) values (?, ?, ?)][Hibernate training,0,1]} #select post in secondary transaction
Query:{[select entityopti0_.id as id1_1_0_, entityopti0_.name as name2_1_0_, entityopti0_.version as version3_1_0_ from post entityopti0_ where entityopti0_.id=?][1]}
Query:{[select comments0_.post_id as post_id1_1_0_, comments0_.comments_id as comments2_2_0_, entityopti1_.id as id1_0_1_, entityopti1_.post_id as post_id3_0_1_, entityopti1_.review as review2_0_1_, entityopti2_.id as id1_1_2_, entityopti2_.name as name2_1_2_, entityopti2_.version as version3_1_2_ from post_comment comments0_ inner join comment entityopti1_ on comments0_.comments_id=entityopti1_.id left outer join post entityopti2_ on entityopti1_.post_id=entityopti2_.id where comments0_.post_id=?][1]} #insert comment in secondary transaction
#optimistic locking post version update in secondary transaction
Query:{[insert into comment (id, review) values (default, ?)][Good post!]}
Query:{[update post set name=?, version=? where id=? and version=?][Hibernate training,1,1,0]}
Query:{[insert into post_comment (post_id, comments_id) values (?, ?)][1,1]} #optimistic locking exception in primary transaction
Query:{[update post set name=?, version=? where id=? and version=?][Hibernate Master Class,1,1,0]}
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.vladmihalcea.hibernate.masterclass.laboratory.concurrency.EntityOptimisticLockingOnBidirectionalParentOwningCollectionTest$Post#1]

双向儿童拥有侧-父母关联测试

#create tables
Query:{[create table comment (id bigint generated by default as identity (start with 1), review varchar(255), post_id bigint, primary key (id))][]}
Query:{[create table post (id bigint not null, name varchar(255), version integer not null, primary key (id))][]}
Query:{[alter table comment add constraint FK_f1sl0xkd2lucs7bve3ktt3tu5 foreign key (post_id) references post][]} #insert post in primary transaction
Query:{[insert into post (name, version, id) values (?, ?, ?)][Hibernate training,0,1]} #select post in secondary transaction
Query:{[select entityopti0_.id as id1_1_0_, entityopti0_.name as name2_1_0_, entityopti0_.version as version3_1_0_ from post entityopti0_ where entityopti0_.id=?][1]} #insert comment in secondary transaction
#post version is not incremented in secondary transaction
Query:{[insert into comment (id, post_id, review) values (default, ?, ?)][1,Good post!]}
Query:{[select count(id) from comment where post_id =?][1]} #update works in primary transaction
Query:{[update post set name=?, version=? where id=? and version=?][Hibernate Master Class,1,1,0]}

否决默认集合版本控制

如果默认的拥有方集合版本控制不适合您的用例,则始终可以使用Hibernate @OptimisticLock注释来取代它。

让我们废除双向parent-owning-side-child关联的默认父版本更新机制:

@Entity(name = "post")
public class Post {...@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)@OptimisticLock(excluded = true)private List<Comment> comments = new ArrayList<Comment>();...public void addComment(Comment comment) {comment.setPost(this);comments.add(comment);}
}   @Entity(name = "comment")
public class Comment {...@ManyToOne@JoinColumn(name = "post_id", insertable = false, updatable = false)private Post post;...
}

这次,子级集合更改不会触发父版本更新:

#create tables
Query:{[create table comment (id bigint generated by default as identity (start with 1), review varchar(255), post_id bigint, primary key (id))][]}
Query:{[create table post (id bigint not null, name varchar(255), version integer not null, primary key (id))][]}
Query:{[create table post_comment (post_id bigint not null, comments_id bigint not null)][]}
Query:{[alter table post_comment add constraint UK_se9l149iyyao6va95afioxsrl  unique (comments_id)][]}
Query:{[alter table comment add constraint FK_f1sl0xkd2lucs7bve3ktt3tu5 foreign key (post_id) references post][]}
Query:{[alter table post_comment add constraint FK_se9l149iyyao6va95afioxsrl foreign key (comments_id) references comment][]}
Query:{[alter table post_comment add constraint FK_6o1igdm04v78cwqre59or1yj1 foreign key (post_id) references post][]} #insert post in primary transaction
Query:{[insert into post (name, version, id) values (?, ?, ?)][Hibernate training,0,1]} #select post in secondary transaction
Query:{[select entityopti0_.id as id1_1_0_, entityopti0_.name as name2_1_0_, entityopti0_.version as version3_1_0_ from post entityopti0_ where entityopti0_.id=?][1]}
Query:{[select comments0_.post_id as post_id1_1_0_, comments0_.comments_id as comments2_2_0_, entityopti1_.id as id1_0_1_, entityopti1_.post_id as post_id3_0_1_, entityopti1_.review as review2_0_1_, entityopti2_.id as id1_1_2_, entityopti2_.name as name2_1_2_, entityopti2_.version as version3_1_2_ from post_comment comments0_ inner join comment entityopti1_ on comments0_.comments_id=entityopti1_.id left outer join post entityopti2_ on entityopti1_.post_id=entityopti2_.id where comments0_.post_id=?][1]} #insert comment in secondary transaction
Query:{[insert into comment (id, review) values (default, ?)][Good post!]}
Query:{[insert into post_comment (post_id, comments_id) values (?, ?)][1,1]} #update works in primary transaction
Query:{[update post set name=?, version=? where id=? and version=?][Hibernate Master Class,1,1,0]}

结论

了解各种建模结构如何影响并发模式非常重要。 递增父版本号时,将考虑拥有方集合的更改,您始终可以使用@OptimisticLock批注绕过它。

  • 代码可在GitHub上获得 。

翻译自: https://www.javacodegeeks.com/2014/11/hibernate-collections-optimistic-locking.html

hibernate乐观锁

hibernate乐观锁_Hibernate Collection乐观锁定相关推荐

  1. java乐观锁实现_Java 乐观锁原理与实战演练

    原标题:Java 乐观锁原理与实战演练 一. 前言 最近在做一个简单审批流程的项目,由于只有固定二级审批所以没有工作流组件,然后就遇到一个审批节点捞单时候,多个人同时审批时候如何保证业务正常运行的问题 ...

  2. java中乐观锁_Java中乐观锁与悲观锁的实现

    锁(locking) 业务逻辑的实现过程中,往往需要保证数据访问的排他性.如在金融系统的日终结算 处理中,我们希望针对某个cut-off时间点的数据进行处理,而不希望在结算进行过程中 (可能是几秒种, ...

  3. mysql乐观锁重试_乐观锁失败重试

    1.乐观锁失败后会报:ObjectOptimisticLockFailureException 2.处理方案:捕获到对应乐观锁失败异常后进行重试,代码参考如下 在写入数据库的时候需要有锁,比如同时写入 ...

  4. mysql内置乐观锁吗_mysql 乐观锁详解

    乐观锁: 乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制.悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性.但随之而来的就是 ...

  5. java 乐观锁 实例_JAVA乐观锁实现-CAS(示例代码)

    是什么 全称compare and swap,一个CPU原子指令,在硬件层面实现的机制,体现了乐观锁的思想. JVM用C语言封装了汇编调用.Java的基础库中有很多类就是基于JNI调用C接口实现了多线 ...

  6. mysql乐观锁重试_乐观锁加重试,并发更新数据库一条记录导致:Lock wait timeout exceeded...

    背景: mysql数据库,用户余额表有一个version(版本号)字段,作为乐观锁. 更新方法有事务控制: @Transactional(rollbackFor = Exception.class) ...

  7. mysql 乐观锁 时间戳_MySQL乐观锁

    乐观锁,大多是基于数据版本(Version)记录机制实现.何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 "version" ...

  8. oracle的乐观锁和悲观锁

    一.问题引出 1. 假设当当网上用户下单买了本书,这时数据库中有条订单号为001的订单,其中有个status字段是'有效',表示该订单是有效的: 2. 后台管理人员查询到这条001的订单,并且看到状态 ...

  9. MySql事务4种隔离级别以及悲观锁和乐观锁

    前言:在那鬼公司呆着发现自己居然把事务给搞明白了. 缘由:公司做的一个项目在进行首页内容显示的时候发现查询结果特别慢,有时候需要一到五分钟才能显示出结果.于是乎,我就顺着SQL语句查询慢的原因找了下去 ...

最新文章

  1. Java大厂技术文档:Redis+Nginx+设计模式+Spring全家桶+Dubbo精选
  2. 开发网络爬虫应该怎样选择爬虫框架?
  3. 微信8.0之后,又悄悄上线新功能:别人“抢不到”的红包!赶紧看看灰度到你了没?...
  4. python IP地址无效化
  5. 拼图项目:一个不完整的难题
  6. Spark Mllib里的如何对单个数据集用斯皮尔曼计算相关系数
  7. Java会话技术之 —— Spring Session
  8. 互联网大厂、架构设计、稳定性、高可用、必备知识点技能,进大厂必备!!快来瞧瞧吧
  9. Android Notification通知详解
  10. 成立一年,openEuler为开源带来哪些改变
  11. python人口普查数据数据分析_Python:第六次全国人口普查数据分析及可视化(pandas、matplotlib)...
  12. Abaqus有限元分析软件介绍
  13. 虚拟机VirtualBox下载与安装、安装Ubuntu超详细图文步骤,对一些配置问题也有所写。
  14. Android文字跑马灯简单实现的三种方法
  15. 任务驱动在中职计算机课中的应用,论任务驱动教学法在中职计算机基础课上的应用...
  16. eNSP第三篇:STP,生成树,xSTP,MSTP,多生成树,交换机工作原理,环路的形成
  17. esxi远程管理端口_如何在 vmware esxi 中开放 VNC功能及端口实现远程管理 完整篇...
  18. linux游戏星际公民,鲜游快报:《星际公民》众筹破3.1亿美元 公布新视频展示新机制...
  19. 电脑缩小,电脑网页缩小了怎么恢复?电脑网页缩小的三种恢复方法
  20. PHP个人逍遥商城系统源码 可商用版

热门文章

  1. P4768-[NOI2018]归程【kruskal重构树,最短路】
  2. 51nod-猴猴吃苹果【线段树】
  3. art-template入门(五)之模板变量
  4. 通用唯一标识码UUID的介绍及使用
  5. JavaScript实现复选框全选与全不选的效果
  6. JQuery 表单校验
  7. mysql分组查询的两类
  8. JAVA基础学习大全(笔记)
  9. spring中stereotype注解Component,Repository,Service,Controller
  10. es dsl多条件组合查询(转)