hibernate中的樂觀鎖和悲觀鎖

hibernate支持兩種鎖:悲觀鎖(Pessimistic Locking)和樂觀鎖(Optimistic Locking)

悲觀鎖:指的是對數據庫數據被外界的修改持保守態度(無論是本系統的事務處理,或者是外部系統的事務處理),在整個數據處理的過程數據都處於鎖定的狀態。hibernate中的悲觀鎖,是依靠數據庫中的鎖機制(因爲只有數據庫層才能控制本系統和外部系統對數據庫的數據操作)。

例如”select * from user where userName=’Johnson’ for update“這條sql鎖定了user表中所有userName=’Johnson’的記錄,本次事務提交之前,外界無法修改這些記錄。

hibernate中的悲觀鎖,也是基於數據庫的鎖機制實現的。

String hqlStr ="from TUser as user where user.name='Lili'";
Query query = session.createQuery(hqlStr);
query.setLockMode("user",LockMode.UPGRADE); //加鎖
List userList = query.list();//執行查詢,獲取數據

上面的代碼中setLockMode第一個參數指定了別名爲user的返回的記錄進行上鎖。
生成的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

相對於悲觀鎖,樂觀鎖的鎖機制就顯得比較寬鬆。悲觀鎖大部分情況依靠數據庫的鎖機制實現,來保證最大程度的獨佔性。但另一方面數據庫的開銷非常大,尤其對於長事務來說。

樂觀鎖大部分是基於數據版本(version)記錄機制實現。即在數據表中增加一個版本標識,讀取出數據時,連帶這個版本標識一起讀出,更新數據的時候,把版本標識加1。將提交版本數據跟數據庫中當前版本信息對比,如果提交的數據中版本號大於數據表當前的版本號,則允許更新,否則認爲是過期數據。

hibernate的樂觀鎖主要有兩種方式:version和時間戳

舉個配置的例子:

    <?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="test.Dir" table="t_dir">  
            <id name="id" type="string" unsaved-value="null">  
             <column name="id_" sql-type="char(32)" not-null="true" />  
             <generator class="uuid.hex" />  
        </id>  
        <!--這裏version節點必須要配在id之後-->
            <version column="version_" name="version" />  
            <property name="name" column="name_" type="string"/>  
            <property name="size" column="size_" type="long" />  
            <many-to-one name="dir" column="pid_" class="test.Dir" />  
        </class>  
    </hibernate-mapping>  

樂觀鎖帶來的負面問題:如果兩個不同的事務同時讀取一條數據並進行更新時,程序會報異常:org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)。這時候就要捕獲異常,然後處理並提醒用戶再次提交。
同樣,樂觀鎖也有侷限性。就是隻控制了本系統的事務併發操作,而外部系統對數據表的操作卻無法控制,此時有個解決辦法就是:在存儲過程裏實現樂觀鎖機制,這樣無論是本系統或是外部系統的事務操作,數據庫都可以控制。所以,在設計階段,儘量考慮到各種情況,究竟是在程序端實現好,還是數據庫端實現比較好。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章