Hibernate事務與併發問題處理(悲觀鎖與樂觀鎖)
Hibernate悲觀鎖:在資料有載入的時候就給其進行加鎖,直到該鎖被釋放掉,其他使用者才可以進行修改;
Hibernate樂觀鎖:在對資料進行修改的時候,對資料才去版本或者時間戳等方式來比較,資料是否一致性來實現加鎖。
我們在使用Hibernate中經常用到當多個人對同一資料同時進行修改的時候,會發生髒資料,造成資料的不一致性,解決辦法是可以通過悲觀鎖和樂觀鎖來實現。
Hibernate悲觀鎖:在資料有載入的時候就給其進行加鎖,直到該鎖被釋放掉,其他使用者才可以進行修改,優點:資料的一致性保持得很好,缺點:不適合多個使用者併發訪問。當一個鎖住的資源不被釋放掉的時候,這個資源永遠不會被其他使用者進行修改,容易造成無限期的等待。
Hibernate樂觀鎖:就是在對資料進行修改的時候,對資料才去版本或者時間戳等方式來比較,資料是否一致性來實現加鎖。優點比較好。
一、在Hibernate悲觀鎖中,只要在載入的時候,才去session中的load方法,進行枷鎖,session.load(****.class,1,LockMode.UPDATE);
Hibernate將事務管理委託給底層的JDBC或者JTA,預設是基於JDBC Transaction的。Hibernate支援“悲觀鎖(Pessimistic Locking)”和“樂觀鎖(Optimistic Locking)”。
Hibernate悲觀鎖對資料被外界修改持保守態度,因此,在整個資料處理過程中,將資料處於鎖定狀態。Hibernate悲觀鎖的實現,往往依靠資料庫提供的鎖機制。Hibernate通過使用資料庫的for update子句實現了悲觀鎖機制。
Hibernate的加鎖模式有:
1. LockMode.NONE:無鎖機制
2. LockMode.WRITE:Hibernate在Insert和Update記錄的時候會自動獲取
3. LockMode.READ:Hibernate在讀取記錄的時候會自動獲取
4. LockMode.UPGRADE:利用資料庫的for update子句加鎖
5. LockMode.UPGRADE_NOWAIT:Oracle的特定實現,利用Oracle的for update nowait子句實現加鎖
二、樂觀鎖大多是基於資料版本(Version)記錄機制實現。Hibernate在其資料訪問引擎中內建了Hibernate樂觀鎖實現,可以通過class描述符的optimistic-lock屬性結合version描述符指定。optimistic-lock屬性有如下可選取值:
1. none:無樂觀鎖
2. version:通過版本機制實現樂觀鎖
3. dirty:通過檢查發生變動過的屬性實現樂觀鎖
4. all:通過檢查所有屬性實現樂觀鎖
例子:
1)Hibernate悲觀鎖:
1>POJO類
- public class PersimisticLocking {
- private int id;
- private String Item;
- private int price;
- //省略setter、getter方法
- }
2>、POJO類的對映檔案
- <?xmlversion="1.0"?>
- <!DOCTYPE hibernate-mapping PUBLIC
- "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
- "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
- <hibernate-mappingpackage="org.apple.hibernate">
- <classname="PersimisticLocking"table="t_persimisticLocking">
- <idname="id">
- <generatorclass="native"/>
- </id>
- <propertyname="item"/>
- <propertyname="price"/>
- </class>
- </hibernate-mapping>
3>、載入測試方法
- public void testLoad1()
- {
- Session session = null;
- try {
- session = HibernateUtil.getSession();
- session.beginTransaction();
- OptimisticLocking o = (OptimisticLocking)session.load(OptimisticLocking.class, 1);
- System.out.println("o.item="+o.getItem());
- System.out.println("o.price="+o.getPrice());
- System.out.println("o.version="+o.getVersion());
- o.setPrice(o.getPrice()-10);
- session.update(o);
- session.beginTransaction().commit();
- } catch (Exception e) {
- // TODO: handle exception
- e.printStackTrace();
- session.beginTransaction().rollback();
- }finally{
- HibernateUtil.closeSession(session);
- }
- }
可以設定另外類似的方法,不枷鎖,先對上面的測試程式碼設定斷點,點debug一部分,再執行不枷鎖的,可以看到,如果上面方法不釋放鎖的話,下面的資料就會造成無限期的等待。
2、Hibernate樂觀鎖:
1>在悲觀鎖的基礎上加入private int version;和相關的setter、getter方法。
2>對映檔案配置在class標籤裡面加入optimistic-lock="version",然後在的id標籤後面加入<version name="version"/>
3>測試方法:
- public void testLoad1()
- {
- Session session = null;
- try {
- session = HibernateUtil.getSession();
- session.beginTransaction();
- OptimisticLocking o = (OptimisticLocking)session.load(OptimisticLocking.class, 1);
- System.out.println("o.item="+o.getItem());
- System.out.println("o.price="+o.getPrice());
- System.out.println("o.version="+o.getVersion());
- o.setPrice(o.getPrice()-10);
- session.update(o);
- session.beginTransaction().commit();
- } catch (Exception e) {
- // TODO: handle exception
- e.printStackTrace();
- session.beginTransaction().rollback();
- }finally{
- HibernateUtil.closeSession(session);
- }
- }
在初始資料的時候,version為0,在沒更新一次version都會在原來的基礎上加1,通過version的版本來實現Hibernate樂觀鎖。
在上面的測試方法裡面複製成另外一個方法,對上面的方法進行設定斷點,然後單步除錯幾部,到NO.11行的時候暫停,此時對複製的另外方法執行,然後再執行完上面的方法,就會丟擲異常,所以,在實際的專案開發中,可以通過對異常進行出來,這樣就會實現併發訪問。