1. 程式人生 > >Hibernate事務與併發問題處理(悲觀鎖與樂觀鎖)

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類

  1. public class PersimisticLocking {  
  2.  private int id;  
  3.  private String Item;  
  4.  private int price;  
  5. //省略setter、getter方法  

2>、POJO類的對映檔案

  1. <?xmlversion="1.0"?>
  2.  <!DOCTYPE hibernate-mapping PUBLIC   
  3.      "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
  4.      "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  5. <hibernate-mappingpackage="org.apple.hibernate">
  6. <classname="PersimisticLocking"table="t_persimisticLocking">
  7. <idname="id">
  8. <generatorclass="native"/>
  9. </id>
  10. <propertyname="item"/>
  11. <propertyname="price"/>
  12. </class>
  13. </hibernate-mapping>

3>、載入測試方法

  1. public void testLoad1()  
  2.      {  
  3.          Session session = null;  
  4.          try {  
  5. session = HibernateUtil.getSession();  
  6.              session.beginTransaction();  
  7.              OptimisticLocking o = (OptimisticLocking)session.load(OptimisticLocking.class, 1);  
  8.              System.out.println("o.item="+o.getItem());  
  9.              System.out.println("o.price="+o.getPrice());  
  10.             System.out.println("o.version="+o.getVersion());  
  11.             o.setPrice(o.getPrice()-10);  
  12.             session.update(o);  
  13.             session.beginTransaction().commit();  
  14.         } catch (Exception e) {  
  15.             // TODO: handle exception  
  16.             e.printStackTrace();  
  17.             session.beginTransaction().rollback();  
  18.         }finally{  
  19.             HibernateUtil.closeSession(session);  
  20.         }  
  21.     } 

可以設定另外類似的方法,不枷鎖,先對上面的測試程式碼設定斷點,點debug一部分,再執行不枷鎖的,可以看到,如果上面方法不釋放鎖的話,下面的資料就會造成無限期的等待。

2、Hibernate樂觀鎖:

1>在悲觀鎖的基礎上加入private int version;和相關的setter、getter方法。

2>對映檔案配置在class標籤裡面加入optimistic-lock="version",然後在的id標籤後面加入<version name="version"/>

3>測試方法:

  1. public void testLoad1()  
  2.      {  
  3.          Session session = null;  
  4.          try {  
  5. session = HibernateUtil.getSession();  
  6.              session.beginTransaction();  
  7.              OptimisticLocking o = (OptimisticLocking)session.load(OptimisticLocking.class, 1);  
  8.              System.out.println("o.item="+o.getItem());  
  9.              System.out.println("o.price="+o.getPrice());  
  10.             System.out.println("o.version="+o.getVersion());  
  11.             o.setPrice(o.getPrice()-10);  
  12.             session.update(o);  
  13.             session.beginTransaction().commit();  
  14.         } catch (Exception e) {  
  15.             // TODO: handle exception  
  16.             e.printStackTrace();  
  17.             session.beginTransaction().rollback();  
  18.         }finally{  
  19.             HibernateUtil.closeSession(session);  
  20.         }  
  21.     } 

在初始資料的時候,version為0,在沒更新一次version都會在原來的基礎上加1,通過version的版本來實現Hibernate樂觀鎖。

在上面的測試方法裡面複製成另外一個方法,對上面的方法進行設定斷點,然後單步除錯幾部,到NO.11行的時候暫停,此時對複製的另外方法執行,然後再執行完上面的方法,就會丟擲異常,所以,在實際的專案開發中,可以通過對異常進行出來,這樣就會實現併發訪問。