hibernate乐观锁_Hibernate Collection乐观锁定
hibernate樂觀鎖
介紹
Hibernate提供了一種樂觀的鎖定機(jī)制 ,即使長時(shí)間通話也可以防止更新丟失 。 結(jié)合實(shí)體存儲(chǔ),跨越多個(gè)用戶請求(擴(kuò)展的持久性上下文或分離的實(shí)體),Hibernate可以保證應(yīng)用程序級(jí)的可重復(fù)讀取 。
臟檢查機(jī)制檢測實(shí)體狀態(tài)更改并增加實(shí)體版本。 盡管始終考慮基本屬性更改,但是Hibernate集合在這方面更加微妙。
擁有與反向收藏
在關(guān)系數(shù)據(jù)庫中,兩個(gè)記錄通過外鍵引用關(guān)聯(lián)。 在這種關(guān)系中,引用記錄是父記錄,而引用行(外鍵側(cè))是子記錄。 非空外鍵只能引用現(xiàn)有的父記錄。
在面向?qū)ο蟮目臻g中,可以在兩個(gè)方向上表示這種關(guān)聯(lián)。 我們可以從孩子到父母有一對多的引用,而父母也可以有一對多的孩子集合。
因?yàn)殡p方都有可能控制數(shù)據(jù)庫外鍵狀態(tài),所以我們必須確保只有一方是此關(guān)聯(lián)的所有者。 僅擁有方狀態(tài)更改會(huì)傳播到數(shù)據(jù)庫。 非持有端歷來稱為逆?zhèn)取?
接下來,我將描述對該關(guān)聯(lián)進(jìn)行建模的最常用方法。
單向父項(xiàng)擁有子項(xiàng)關(guān)聯(lián)映射
只有父方具有@OneToMany非逆子級(jí)集合。 子實(shí)體根本不引用父實(shí)體。
@Entity(name = "post") public class Post {...@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)private List<Comment> comments = new ArrayList<Comment>();... }單向父-子-子-子組件關(guān)聯(lián)映射映射
子端不一定總是必須是實(shí)體,我們可以將其建模為組件類型 。 一個(gè)Embeddable對象(組件類型)可能同時(shí)包含基本類型和關(guān)聯(lián)映射,但永遠(yuǎn)不能包含@Id。 可嵌入對象及其擁有的實(shí)體將被持久保存/刪除。
父級(jí)具有@ElementCollection子級(jí)關(guān)聯(lián)。 子實(shí)體只能通過不可查詢的特定于 Hibernate的@Parent批注來引用父實(shí)體。
@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;... }雙向父子側(cè)子關(guān)聯(lián)映射
父級(jí)是擁有方,因此它有一個(gè)@OneToMany非逆(不包含mappingBy指令)子級(jí)集合。 子實(shí)體通過@ManyToOne關(guān)聯(lián)引用父實(shí)體,該關(guān)聯(lián)既不可插入也不可更新:
@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;... }雙向兒童擁有側(cè)-父母關(guān)聯(lián)映射
子實(shí)體通過引用父實(shí)體@ManyToOne協(xié)會(huì)和家長有一個(gè)的mappedBy @OneToMany孩子集合。 父側(cè)是反側(cè),因此僅@ManyToOne狀態(tài)更改會(huì)傳播到數(shù)據(jù)庫。
即使只有一個(gè)擁有的一方,通過使用add / removeChild()方法使雙方保持同步始終是一個(gè)好習(xí)慣。
@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; ... }單向兒童擁有側(cè)父母關(guān)系映射
子實(shí)體通過@ManyToOne關(guān)聯(lián)引用父實(shí)體。 父級(jí)沒有@OneToMany子級(jí)集合,因此子級(jí)實(shí)體成為所有者。 此關(guān)聯(lián)映射類似于關(guān)系數(shù)據(jù)外鍵鏈接。
@Entity(name = "comment") public class Comment {...@ManyToOneprivate Post post; ... }集合版本控制
JPA 2.1規(guī)范的3.4.2部分將樂觀鎖定定義為:
將對象寫入數(shù)據(jù)庫時(shí)??,持久性提供程序運(yùn)行時(shí)會(huì)更新version屬性。 版本檢查中包括所有非關(guān)系字段和適當(dāng)?shù)年P(guān)系以及實(shí)體所擁有的所有關(guān)系[35]。
[35]這包括在聯(lián)接表中維護(hù)的擁有的關(guān)系注意:只有擁有方的子級(jí)集合可以更新父級(jí)版本。
測試時(shí)間
讓我們測試一下父子關(guān)聯(lián)類型如何影響父版本。 因?yàn)槲覀儗ψ蛹?jí)集合的臟檢查感興趣,所以將跳過單向的子級(jí)擁有方-父級(jí)關(guān)聯(lián),因?yàn)樵谶@種情況下,父級(jí)不包含子級(jí)集合。
測試用例
以下測試用例將用于所有集合類型用例:
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;}}); }單向父母所有子女的關(guān)聯(lián)測試
#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]單向父-子-子組件關(guān)聯(lián)測試
#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]雙向父母擁有-子-孩子關(guān)聯(lián)測試
#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]雙向兒童擁有側(cè)-父母關(guān)聯(lián)測試
#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]}否決默認(rèn)集合版本控制
如果默認(rèn)的擁有方集合版本控制不適合您的用例,則始終可以使用Hibernate @OptimisticLock注釋來取代它。
讓我們廢除雙向parent-owning-side-child關(guān)聯(lián)的默認(rèn)父版本更新機(jī)制:
@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;... }這次,子級(jí)集合更改不會(huì)觸發(fā)父版本更新:
#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]}結(jié)論
了解各種建模結(jié)構(gòu)如何影響并發(fā)模式非常重要。 遞增父版本號(hào)時(shí),將考慮擁有方集合的更改,您始終可以使用@OptimisticLock批注繞過它。
- 代碼可在GitHub上獲得 。
翻譯自: https://www.javacodegeeks.com/2014/11/hibernate-collections-optimistic-locking.html
hibernate樂觀鎖
總結(jié)
以上是生活随笔為你收集整理的hibernate乐观锁_Hibernate Collection乐观锁定的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 海马助手安卓版下载(海马助手安卓)
- 下一篇: openshift_在OpenShift