Hibernate悲觀鎖和樂(lè)觀鎖實(shí)例詳解
本文研究的主要是Hibernate悲觀鎖和樂(lè)觀鎖的全部?jī)?nèi)容,具體介紹如下。
悲觀鎖
悲觀鎖通常是由數(shù)據(jù)庫(kù)機(jī)制實(shí)現(xiàn)的,在整個(gè)過(guò)程中把數(shù)據(jù)鎖住(查詢(xún)時(shí)),只要事物不釋放(提交/回滾),那么任何用戶(hù)都不能查看或修改。
下面我們通過(guò)一個(gè)案例來(lái)說(shuō)明。

案例:假設(shè)貨物庫(kù)存為1000,當(dāng)核算員1取出了數(shù)據(jù)準(zhǔn)備修改,但臨時(shí)有事,就走了。期間核算員2取出了數(shù)據(jù)把數(shù)量減去200,然后核算員1回來(lái)了把剛才取出的數(shù)量減去200,這就出現(xiàn)了一個(gè)問(wèn)題,核算員1并沒(méi)有在800的基礎(chǔ)上做修改。這就是所謂的更新丟失,采用悲觀鎖可以解決。
Inventory.java:
public class Inventory {
/* 存貨編號(hào) */
private String itemNo;
/* 存貨名稱(chēng) */
private String itemName;
/* 存貨數(shù)量 */
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">
<!-- 主鍵手動(dòng)分配 -->
<id name="itemNo">
<generator class="assigned"/>
</id>
<!-- 映射屬性 -->
<property name="itemName"/>
<property name="quantity"/>
</class>
</hibernate-mapping>
測(cè)試類(lèi):
核算員1通過(guò)悲觀鎖的方式加載數(shù)據(jù),并對(duì)數(shù)據(jù)進(jìn)行修改!
public void testLoad1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
/*在加載的時(shí)候就加上一把悲觀鎖,讓其他用戶(hù)都無(wú)法訪問(wèn)*/
Inventory inv = (Inventory) session.load(Inventory.class, "1001", LockMode.UPGRADE);
/*獲取數(shù)據(jù)*/
System.out.println("opt1-->itemNo=" + inv.getItemNo());
System.out.println("opt1-->itemName=" + inv.getItemName());
System.out.println("opt1-->quantity=" + inv.getQuantity());
/*數(shù)量減去200*/
inv.setQuantity(inv.getQuantity() - 200);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
HibernateUtils.closeSession(session);
}
}
核算員2和核算員1的操作相同,都是對(duì)數(shù)據(jù)庫(kù)中的數(shù)據(jù)進(jìn)行修改!
public void testLoad2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
/*在加載數(shù)據(jù)的時(shí)候就加上一把鎖,讓其他人無(wú)法獲取數(shù)據(jù)*/
Inventory inv = (Inventory) session.load(Inventory.class, "1001", LockMode.UPGRADE);
/*獲取真實(shí)數(shù)據(jù)*/
System.out.println("opt2-->itemNo=" + inv.getItemNo());
System.out.println("opt2-->itemName=" + inv.getItemName());
System.out.println("opt2-->quantity=" + inv.getQuantity());
/*庫(kù)存減去200*/
inv.setQuantity(inv.getQuantity() - 200);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally {
HibernateUtils.closeSession(session);
}
}
注:兩個(gè)核算員做的操作相同,如果加了悲觀鎖之后,核算員取出了數(shù)據(jù)并對(duì)數(shù)據(jù)進(jìn)行修改,在核算員1沒(méi)有提交事物之前,核算員2是不能對(duì)數(shù)據(jù)進(jìn)行訪問(wèn)的,只能處于等待狀態(tài)。知道核算員1把事物提交了之后,核算員2才有機(jī)會(huì)對(duì)數(shù)據(jù)庫(kù)中的數(shù)據(jù)進(jìn)行操作。
通過(guò)上面悲觀鎖的案例我們可以發(fā)現(xiàn),悲觀鎖最大的好處就是可以防止更新丟失,當(dāng)核算員1在處理數(shù)據(jù)的時(shí)候,核算員2只能處于等待狀態(tài),只有核算員1提交了事物之后,核算員2才有機(jī)會(huì)修改數(shù)據(jù)。但是也存在一個(gè)很大的問(wèn)題,那就是,如果核算員1將數(shù)據(jù)查詢(xún)出來(lái)后人就走掉了,那么其他人就得等上大半天,非常浪費(fèi)時(shí)間,為了解決這個(gè)問(wèn)題,我們可以使用樂(lè)觀鎖。
樂(lè)觀鎖
樂(lè)觀鎖并不是真正意義上的鎖,大多數(shù)情況下是采用數(shù)據(jù)版本(version)的方式實(shí)現(xiàn),一般在數(shù)據(jù)庫(kù)中加入一個(gè)version字段,在讀取數(shù)據(jù)的時(shí)候就將version讀取出來(lái),在保存數(shù)據(jù)的時(shí)候判斷version的值是否小于數(shù)據(jù)庫(kù)的version值,如果小于則不予更新,否則給予更新。
樂(lè)觀鎖下的javaBean設(shè)置,Inventory.java:
public class Inventory {
/*存貨編號(hào)*/
private String itemNo;
/*存貨名稱(chēng)*/
private String itemName;
/*存貨數(shù)量*/
private int quantity;
/*數(shù)據(jù)版本*/
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標(biāo)簽中加上optimistc-lock屬性,其值為版本信心 -->
<class name="com.lixue.bean.Inventory" table="t_inventory" optimistic-lock="version">
<!-- 主鍵映射 -->
<id name="itemNo">
<generator class="assigned"/>
</id>
<!-- 數(shù)據(jù)版本,必須在主鍵后面的位置 -->
<version name="version"/>
<!-- 基本屬性映射 -->
<property name="itemName"/>
<property name="quantity"/>
</class>
</hibernate-mapping>
注:使用樂(lè)觀鎖的映射文件有規(guī)定即version字段的映射必須在主鍵ID之后第一個(gè)被映射。
測(cè)試:
核算員1在樂(lè)觀鎖的情況下處理數(shù)據(jù):
public void testLoad1() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
/*樂(lè)觀鎖下加載數(shù)據(jù)*/
Inventory inv = (Inventory)session.load(Inventory.class, "1001");
/*實(shí)際獲取數(shù)據(jù)*/
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());
/*數(shù)量減去200*/
inv.setQuantity(inv.getQuantity() - 200);
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
核算員2在樂(lè)觀鎖的情況下處理數(shù)據(jù)(核算員2可以在核算員1未提交數(shù)據(jù)的前提下處理數(shù)據(jù))
public void testLoad2() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
/*樂(lè)觀鎖下加載數(shù)據(jù)*/
Inventory inv = (Inventory)session.load(Inventory.class, "1001");
/*實(shí)際獲取數(shù)據(jù)*/
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());
/*數(shù)量減去200*/
inv.setQuantity(inv.getQuantity() - 200);
session.getTransaction().commit();
}catch(Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}finally {
HibernateUtils.closeSession(session);
}
}
注:在核算員取出數(shù)據(jù)將數(shù)量減去200之后并未提交的前提下,核算員2也可以操作數(shù)據(jù),這就有別于悲觀鎖,當(dāng)核算員2操作了數(shù)據(jù)并且提交之后,數(shù)據(jù)庫(kù)中數(shù)據(jù)版本version就會(huì)加1,那么當(dāng)核算員1在回來(lái)進(jìn)行事物提交時(shí)就會(huì)出現(xiàn)錯(cuò)誤提示即數(shù)據(jù)已更新,請(qǐng)重新加載。
總結(jié)
悲觀鎖會(huì)影響高并發(fā),所以用樂(lè)觀鎖比較好。
以上就是本文關(guān)于Hibernate悲觀鎖和樂(lè)觀鎖實(shí)例詳解的全部?jī)?nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專(zhuān)題,如有不足之處,歡迎留言指出。感謝朋友們對(duì)本站的支持!
相關(guān)文章
java線程池的四種創(chuàng)建方式詳細(xì)分析
這篇文章主要介紹了java線程池的四種創(chuàng)建方式詳細(xì)分析,連接池是創(chuàng)建和管理一個(gè)連接的緩沖池的技術(shù),這些連接準(zhǔn)備好被任何需要它們的線程使用2022-07-07
Java編程細(xì)節(jié)重構(gòu)之為什么if-else不是好代碼詳析
這篇文章主要給大家介紹了關(guān)于Java編程細(xì)節(jié)重構(gòu)之為什么if-else不是好代碼的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)些學(xué)習(xí)吧2018-09-09
Java實(shí)體類(lèi)中Set按照對(duì)象的某個(gè)字段對(duì)set排序
這篇文章主要介紹了Java實(shí)體類(lèi)中Set按照對(duì)象的某個(gè)字段對(duì)set排序,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-06-06
如何使用Playwright對(duì)Java API實(shí)現(xiàn)自動(dòng)視覺(jué)測(cè)試
這篇文章主要介紹了如何使用Playwright對(duì)Java API實(shí)現(xiàn)自動(dòng)視覺(jué)測(cè)試,幫助大家更好的理解和使用Playwright,感興趣的朋友可以了解下2021-01-01
如何實(shí)現(xiàn)自定義SpringBoot的Starter組件
這篇文章主要介紹了實(shí)現(xiàn)自定義SpringBoot的Starter組件的示例代碼,想要自定義starter組件,首先要了解springboot是如何加載starter的,也就是springboot的自動(dòng)裝配機(jī)制原理,本文結(jié)合示例代碼詳細(xì)講解,需要的朋友可以參考下2023-02-02
Spring Boot集成Druid數(shù)據(jù)庫(kù)連接池
這篇文章主要介紹了Spring Boot集成Druid數(shù)據(jù)庫(kù)連接池,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04
java接口私有方法實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了java接口私有方法實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
深入探討Java應(yīng)用性能監(jiān)控與調(diào)優(yōu)的工具鏈構(gòu)建
這篇文章主要來(lái)和大家將深入探討Java應(yīng)用性能監(jiān)控與調(diào)優(yōu)的完整工具鏈,從傳統(tǒng)的單機(jī)分析工具JProfiler到現(xiàn)代化的分布式監(jiān)控系統(tǒng)Prometheus,希望能幫助開(kāi)發(fā)者和運(yùn)維人員構(gòu)建全方位的性能監(jiān)控體系2025-06-06

