本文研究的主要是Hibernate悲觀鎖和樂觀鎖的全部內容,具體介紹如下。
悲觀鎖通常是由數據庫機制實現的,在整個過程中把數據鎖住(查詢時),只要事物不釋放(提交/回滾),那麼任何用戶都不能查看或修改。
下面我們通過一個案例來說明。
案例:假設貨物庫存為1000,當核算員1取出了數據準備修改,但臨時有事,就走了。期間核算員2取出了數據把數量減去200,然後核算員1回來了把剛才取出的數量減去200,這就出現了一個問題,核算員1並沒有在800的基礎上做修改。這就是所謂的更新丟失,採用悲觀鎖可以解決。
Inventory.java:
public class Inventory { /* 存貨編號*/ private String itemNo; /* 存貨名稱*/ private String itemName; /* 存貨數量*/ private int quantity; //省略setter和getter方法}Inventory.hbm.xml:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="com.lixue.bean.Inventory" table="t_inventory"> <!-- 主鍵手動分配--> <id name="itemNo"> <generator/> </id> <!-- 映射屬性--> <property name="itemName"/> <property name="quantity"/> </class> </hibernate-mapping>
測試類:
核算員1通過悲觀鎖的方式加載數據,並對數據進行修改!
public void testLoad1() { Session session = null; try { session = HibernateUtils.getSession(); session.beginTransaction(); /*在加載的時候就加上一把悲觀鎖,讓其他用戶都無法訪問*/ Inventory inv = (Inventory) session.load(Inventory.class, "1001", LockMode.UPGRADE); /*獲取數據*/ System.out.println("opt1-->itemNo=" + inv.getItemNo()); System.out.println("opt1-->itemName=" + inv.getItemName()); System.out.println("opt1-->quantity=" + inv.getQuantity()); /*數量減去200*/ inv.setQuantity(inv.getQuantity() - 200); session.getTransaction().commit(); } catch (Exception e) { e.printStackTrace(); session.getTransaction().rollback(); } finally { HibernateUtils.closeSession(session); } }核算員2和核算員1的操作相同,都是對數據庫中的數據進行修改!
public void testLoad2() { Session session = null; try { session = HibernateUtils.getSession(); session.beginTransaction(); /*在加載數據的時候就加上一把鎖,讓其他人無法獲取數據*/ Inventory inv = (Inventory) session.load(Inventory.class, "1001", LockMode.UPGRADE); /*獲取真實數據*/ System.out.println("opt2-->itemNo=" + inv.getItemNo()); System.out.println("opt2-->itemName=" + inv.getItemName()); System.out.println("opt2-->quantity=" + inv.getQuantity()); /*庫存減去200*/ inv.setQuantity(inv.getQuantity() - 200); session.getTransaction().commit(); } catch (Exception e) { e.printStackTrace(); session.getTransaction().rollback(); } finally { HibernateUtils.closeSession(session); } }注:兩個核算員做的操作相同,如果加了悲觀鎖之後,核算員取出了數據並對數據進行修改,在核算員1沒有提交事物之前,核算員2是不能對數據進行訪問的,只能處於等待狀態。知道核算員1把事物提交了之後,核算員2才有機會對數據庫中的數據進行操作。
通過上面悲觀鎖的案例我們可以發現,悲觀鎖最大的好處就是可以防止更新丟失,當核算員1在處理數據的時候,核算員2只能處於等待狀態,只有核算員1提交了事物之後,核算員2才有機會修改數據。但是也存在一個很大的問題,那就是,如果核算員1將數據查詢出來後人就走掉了,那麼其他人就得等上大半天,非常浪費時間,為了解決這個問題,我們可以使用樂觀鎖。
樂觀鎖並不是真正意義上的鎖,大多數情況下是採用數據版本(version)的方式實現,一般在數據庫中加入一個version字段,在讀取數據的時候就將version讀取出來,在保存數據的時候判斷version的值是否小於數據庫的version值,如果小於則不予更新,否則給予更新。
樂觀鎖下的javaBean設置, Inventory.java:
public class Inventory { /*存貨編號*/ private String itemNo; /*存貨名稱*/ private String itemName; /*存貨數量*/ private int quantity; /*數據版本*/ private int version; //省略setter和getter方法}Inventory.hbm.xml:
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <!-- 在class標籤中加上optimistc-lock屬性,其值為版本信心--> <class name="com.lixue.bean.Inventory" table="t_inventory" optimistic-lock="version"> <!-- 主鍵映射--> <id name="itemNo"> <generator/> </id> <!-- 數據版本,必須在主鍵後面的位置--> <version name="version"/> <!-- 基本屬性映射--> <property name="itemName"/> <property name="quantity"/> </class> </hibernate-mapping>
注:使用樂觀鎖的映射文件有規定即version字段的映射必須在主鍵ID之後第一個被映射。
核算員1在樂觀鎖的情況下處理數據:
public void testLoad1() { Session session = null; try { session = HibernateUtils.getSession(); session.beginTransaction(); /*樂觀鎖下加載數據*/ Inventory inv = (Inventory)session.load(Inventory.class, "1001"); /*實際獲取數據*/ System.out.println("opt1-->itemNo=" + inv.getItemNo()); System.out.println("opt1-->itemName=" + inv.getItemName()); System.out.println("opt1-->version=" + inv.getVersion()); System.out.println("opt1-->quantity=" + inv.getQuantity()); /*數量減去200*/ inv.setQuantity(inv.getQuantity() - 200); session.getTransaction().commit(); }catch(Exception e) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } }核算員2在樂觀鎖的情況下處理數據(核算員2可以在核算員1未提交數據的前提下處理數據)
public void testLoad2() { Session session = null; try { session = HibernateUtils.getSession(); session.beginTransaction(); /*樂觀鎖下加載數據*/ Inventory inv = (Inventory)session.load(Inventory.class, "1001"); /*實際獲取數據*/ System.out.println("opt2-->itemNo=" + inv.getItemNo()); System.out.println("opt2-->itemName=" + inv.getItemName()); System.out.println("opt2-->version=" + inv.getVersion()); System.out.println("opt2-->quantity=" + inv.getQuantity()); /*數量減去200*/ inv.setQuantity(inv.getQuantity() - 200); session.getTransaction().commit(); }catch(Exception e) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } }注:在核算員取出數據將數量減去200之後並未提交的前提下,核算員2也可以操作數據,這就有別於悲觀鎖,當核算員2操作了數據並且提交之後,數據庫中數據版本version就會加1,那麼當核算員1在回來進行事物提交時就會出現錯誤提示即數據已更新,請重新加載。
悲觀鎖會影響高並發,所以用樂觀鎖比較好。
以上就是本文關於Hibernate悲觀鎖和樂觀鎖實例詳解的全部內容,希望對大家有所幫助。感興趣的朋友可以繼續參閱本站其他相關專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!