休眠CascadeType.LOCK陷阱
介紹
引入了Hibernate 顯式鎖定支持以及Cascade Types之后 ,就該分析CascadeType.LOCK行為了。
休眠鎖定請求觸發(fā)內(nèi)部LockEvent 。 關(guān)聯(lián)的DefaultLockEventListener可以將鎖定請求級聯(lián)到鎖定實(shí)體子級。
由于CascadeType.ALL也包括CascadeType.LOCK ,因此當(dāng)鎖定請求從父級實(shí)體傳播到子級實(shí)體時(shí),值得理解。
測試時(shí)間
我們將從以下實(shí)體模型開始:
Post是PostDetail一對一關(guān)聯(lián)和Comment一對多關(guān)聯(lián)的Parent實(shí)體,這些關(guān)聯(lián)用CascadeType.ALL標(biāo)記:
@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;所有即將到來的測試用例將使用以下實(shí)體模型圖:
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); });鎖定管理實(shí)體
將受管實(shí)體加載到當(dāng)前正在運(yùn)行的持久性上下文中,并將所有實(shí)體狀態(tài)更改轉(zhuǎn)換為DML語句。
當(dāng)托管父實(shí)體被鎖定時(shí):
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); });只有父實(shí)體被鎖定,因此可以防止級聯(lián):
select id from Post where id = 1 for updateHibernate定義了一個(gè)范圍 LockOption ,該范圍 (根據(jù)JavaDocs)應(yīng)允許將鎖定請求傳播到Child實(shí)體:
“范圍”是JPA定義的術(shù)語。 基本上,這是關(guān)聯(lián)鎖定的級聯(lián)。
session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)) .setScope(true) .lock(post);設(shè)置范圍標(biāo)志不會改變?nèi)魏螙|西,只有被管理實(shí)體被鎖定:
select id from Post where id = 1 for update鎖定獨(dú)立實(shí)體
除了實(shí)體鎖定之外,鎖定請求還可以重新關(guān)聯(lián)分離的實(shí)體。 為了證明這一點(diǎn),我們將在鎖定實(shí)體請求之前和之后檢查Post實(shí)體圖:
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如何用于分離的實(shí)體:
//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()); });鎖定請求重新關(guān)聯(lián)了實(shí)體圖,但是當(dāng)前正在運(yùn)行的Hibernate Session并未意識到處于分離狀態(tài)的實(shí)體變臟了。 僅在不強(qiáng)制執(zhí)行UPDATE或選擇當(dāng)前數(shù)據(jù)庫狀態(tài)進(jìn)行進(jìn)一步比較的情況下,才重新連接實(shí)體。
一旦對實(shí)體進(jìn)行管理, 臟檢查機(jī)制將檢測到任何進(jìn)一步的更改,并且刷新也會傳播重新附加的更改。 如果在管理實(shí)體時(shí)未發(fā)生任何更改,則不會安排該實(shí)體進(jìn)行刷新。
如果要確保分離的實(shí)體狀態(tài)始終與數(shù)據(jù)庫同步,則需要使用merge或update 。
當(dāng)scope選項(xiàng)設(shè)置為true時(shí),分離的實(shí)體傳播lock選項(xiàng):
session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)) .setScope(true) .lock(post);Post實(shí)體鎖定事件會傳播到所有Child實(shí)體(因?yàn)槲覀冋谑褂肅ascadeType.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結(jié)論
鎖級聯(lián)不是簡單明了或直觀的。 顯式鎖定需要勤奮(我們獲取的鎖越多,死鎖的機(jī)會就越大),并且無論如何,最好保留對Child實(shí)體鎖傳播的完全控制權(quán)。 因此,與并發(fā)編程最佳實(shí)踐類似,手動鎖定優(yōu)于自動鎖定傳播。
- 代碼可在GitHub上獲得 。
翻譯自: https://www.javacodegeeks.com/2015/03/hibernate-cascadetype-lock-gotchas.html
總結(jié)
以上是生活随笔為你收集整理的休眠CascadeType.LOCK陷阱的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 台式电脑可以当显卡坞吗(显卡坞能装什么显
- 下一篇: 电脑画图作品简单(电脑画图作品图片大全)