hibernate乐观锁_Hibernate Collection乐观锁定
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乐观锁定的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 海马助手安卓版下载(海马助手安卓)
- 下一篇: ddos攻击怎么查攻击者(ddos攻击怎