日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

(13) 悲观锁和乐观锁解决hibernate并发(转)

發布時間:2024/1/23 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 (13) 悲观锁和乐观锁解决hibernate并发(转) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言:
?做項目時由于業務邏輯的需要,必須對數據表的一行或多行加入行鎖,舉個最簡單的例子,圖書借閱系統。假設 id=1 的這本書庫存為 1 ,但是有 2 個人同時來借這本書,此處的邏輯為

Select restnum from book where id =1 ; -- 如果 restnum 大于 0 ,執行 update Update book set restnum=restnum-1 where id=1 ;
問題就來了,當 2 個人同時來借的時候,有可能第一個人執行 select 語句的時候,第二個人插了進來,在第一個人沒來得及更新 book 表的時候,第二個人查到數據了,其實是臟數據,因為第一個人會把 restnum 值減 1 ,因此第二個人本來應該是查到 id=1 的書 restnum 為 0 了,因此不會執行 update ,而會告訴它 id=1 的書沒有庫存 了,可是數據庫哪懂這些,數據庫只負責執行一條條 SQL 語句,它才不管中間有沒有其他 sql 語句插進來,它也不知道要把一個 session的 sql 語句執行完再執行另一個 session 的。因此會導致并發的時候 restnum 最后的結果為 -1 ,顯然這是不合理的,所以,才出現鎖的概念, Mysql 使用 innodb 引擎可以通過索引 對數據行加鎖。


鎖( locking ),這個概念在我們學習多線程的時候曾經接觸過,其實這里的鎖和多線程里面處理并發的鎖是一個道理,都是暴力的把資源歸為自己所有。這里我們用到鎖的目的就是通過一些機制來保證一些數據在某個操作過程中不會被外界修改,這樣的機制,在這里,也就是所謂的“鎖”,即給我們選定的目標數據上鎖,使其無法被其他程序修改。Hibernate 支持兩種鎖機制:即通常所說的“悲觀鎖(Pessimistic Locking )”和“樂觀鎖( Optimistic Locking )”。
悲觀鎖( Pessimistic Locking )
悲觀鎖,正如其名,他是對數據庫而言的,數據庫悲觀了,他感覺每一個對他操作的程序都有可能產生并發。它指的是對數據被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度,因此,在整個數據處理過程中,將數據處于鎖定狀態。悲觀鎖的實現,往往依靠數據庫提供的鎖機制(也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會修改數據)。
一個典型的倚賴數據庫的悲觀鎖調用:

select * from account wherename=”Erica” forupdate
這條 sql 語句鎖定了 account 表中所有符合檢索條件(name=”Erica”)的記錄。本次事務提交之前(事務提交時會釋放事務過程中的鎖),外界無法修改這些記錄。Hibernate 的悲觀鎖,也是基于數據庫的鎖機制實現。
下面的代碼實現了對查詢記錄的加鎖:

String hqlStr ="from TUser as user where user.name=‘Erica‘"; Query query = session.createQuery(hqlStr); query.setLockMode("user",LockMode.UPGRADE); // 加鎖 List userList = query.list();// 執行查詢,獲取數據
query.setLockMode 對查詢語句中,特定別名所對應的記錄進行加鎖(我們為TUser 類指定了一個別名“user”),這里也就是對返回的所有 user 記錄進行加鎖。
觀察運行期Hibernate 生成的 SQL 語句:

select tuser0_.id as id, tuser0_.name as name,tuser0_.group_id as group_id, tuser0_.user_type as user_type, tuser0_.sex as sex from t_user tuser0_ where (tuser0_.name=‘Erica‘ )for update
這里Hibernate 通過使用數據庫的 for update 子句實現了悲觀鎖機制。


Hibernate 的加鎖模式有:
LockMode.NONE :無鎖機制。
LockMode.WRITE :Hibernate 在 Insert 和 Update 記錄的時候會自動獲取。
LockMode.READ :Hibernate 在讀取記錄的時候會自動獲取。


以上這三種鎖機制一般由 Hibernate 內部使用,如Hibernate 為了保證 Update過程中對象不會被外界修改,會在 save 方法實現中自動為目標對象加上 WRITE 鎖。
LockMode.UPGRADE :利用數據庫的 for update 子句加鎖。
LockMode. UPGRADE_NOWAIT : Oracle 的特定實現,利用 Oracle 的 for update nowait 子句實現加鎖。
上面這兩種鎖機制是我們在應用層較為常用的,加鎖一般通過以下方法實現:
Criteria.setLockMode
Query.setLockMode
Session.lock

注意,只有在查詢開始之前(也就是 Hiberate 生成 SQL 之前)設定加鎖,才會真正通過數據庫的鎖機制進行加鎖處理,否則,數據已經通過不包含 for update 子句的 Select SQL 加載進來,所謂數據庫加鎖也就無從談起。


在Hibernate使用悲觀鎖十分容易,但實際應用中悲觀鎖是很少被使用的,因為它大大限制了并發性,并且利用數據庫底層來維護鎖,這樣大大降低了應用程序的效率。
下面我們來看一下hibernateAPI中提供的兩個get方法:
Get(Classclazz,Serializable id,LockMode lockMode)
Get(Classclazz,Serializable id,LockOptions lockOptions ?)
?
可以看到get方法第三個參數"lockMode"或"lockOptions",注意在Hibernate3.6以上的版本中"LockMode"已經不建議使用。方法的第三個參數就是用來設置悲觀鎖的,使用第三個參數之后,我們每次發送的SQL語句都會加上"for update"用于告訴數據庫鎖定相關數據。LockMode參數選擇UPGRADE選項,就會開啟悲觀鎖。
?
樂觀鎖(Optimistic Locking)
? ? ? ?相對悲觀鎖而言,樂觀鎖機制采取了更加寬松的加鎖機制。悲觀鎖大多數情況下依靠數據庫的鎖機制實現,以保證操作最大程度的獨占性。但隨之而來的就是數據庫性能的大量開銷,特別是對長事務而言,這樣的開銷往往無法承受。樂觀鎖機制在一定程度上解決了這個問題。樂觀鎖,大多是基于數據版本(Version)記錄機制實現。何謂數據版本?即為數據增加一個版本標識,在基于數據庫表的版本解決方案中,一般是通過為數據庫表增加一個"version"字段來實現。
  樂觀鎖的工作原理:讀取出數據時,將此版本號一同讀出,之后更新時,對此版本號加一。此時,將提交數據的版本數據與數據庫表對應記錄的當前版本信息進行比對,如果提交的數據版本號大于數據庫表當前版本號,則予以更新,否則認為是過期數據。
Hibernate為樂觀鎖提供了3中實現:
1. 基于version
2. 基于timestamp
3. 為遺留項目添加添加樂觀鎖
?
配置基于version的樂觀鎖:

<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC"-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <classnameclassname="com.bzu.hibernate.pojos.People"table="people"> <idnameidname="id"type="string"> <columnnamecolumnname="id"></column> <generatorclassgeneratorclass="uuid"></generator> </id> <!--version標簽用于指定表示版本號的字段信息--> <versionnameversionname="version"column="version"type="integer"></version> <propertynamepropertyname="name"column="name"type="string"></property> </class> </hibernate-mapping>
注:不要忘記在實體類添加屬性version
?
配置基于timestamp的樂觀鎖:

<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC"-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <classnameclassname="com.suxiaolei.hibernate.pojos.People"table="people"> <id name="id"type="string"> <column name="id"></column> <generator class="uuid"></generator> </id> <!--timestamp標簽用于指定表示版本號的字段信息--> <timestamp name="updateDate"column="updateDate"></timestamp> <propertynamepropertyname="name"column="name"type="string"></property> </class> </hibernate-mapping>
下面我們就模擬多個session,基于version的來進行一下測試:

/* * 模擬多個session操作student數據表 */ Sessionsession1=sessionFactory.openSession(); Session session2=sessionFactory.openSession(); Studentstu1=(Student)session1.createQuery("from Student s wheres.name='tom11'").uniqueResult(); Studentstu2=(Student)session2.createQuery("from Student s wheres.name='tom11'").uniqueResult(); //這時候,兩個版本號是相同的 System.out.println("v1="+stu1.getVersion()+"--v2="+stu2.getVersion()); Transactiontx1=session1.beginTransaction(); stu1.setName("session1"); tx1.commit(); //這時候,兩個版本號是不同的,其中一個的版本號遞增了 System.out.println("v1="+stu1.getVersion()+"--v2="+stu2.getVersion()); Transactiontx2=session2.beginTransaction(); stu2.setName("session2"); tx2.commit();


運行結果:

Hibernate: insert into studentVersion (ver, name,id) values (?, ?, ?) Hibernate: select student0_.id as id0_, student0_.ver as ver0_, student0_.nameas name0_ from studentVersion student0_ where student0_.name='tom11' Hibernate: select student0_.id as id0_, student0_.ver as ver0_, student0_.nameas name0_ from studentVersion student0_ where student0_.name='tom11' v1=0--v2=0 Hibernate: update studentVersion set ver=?, name=? where id=? and ver=? v1=1--v2=0 Hibernate: update studentVersion set ver=?, name=? where id=? and ver=? Exception in thread "main" org.hibernate.StaleObjectStateException:Row was updated or deleted by another transaction (or unsaved-value mapping wasincorrect): [Version.Student#4028818316cd6b460116cd6b50830001]

?可以看到,第二個“用戶”session2修改數據時候,記錄的版本號已經被session1更新過了,所以拋出了紅色的異常,我們可以在實際應用中處理這個異常,例如在處理中重新讀取數據庫中的數據,同時將目前的數據與數據庫中的數據展示出來,讓使用者有機會比較一下,或者設計程序自動讀取新的數據




總結

以上是生活随笔為你收集整理的(13) 悲观锁和乐观锁解决hibernate并发(转)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。