休眠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 updateHibernate定義了一個范圍 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陷阱的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 台式电脑可以当显卡坞吗(显卡坞能装什么显
- 下一篇: 电脑画图作品简单(电脑画图作品图片大全)