標籤:style class blog code java http
上篇文章詳細討論了hql的各種查詢方法,在討論過程中寫了程式碼範例,hql的查詢方法類似於sql,查詢的方法比較簡單,有sql基礎的開發人員在使用hql時就會變得相當的簡單。Hibernate在操作資料庫的同時也提供了對資料庫操作的限制方法,這種方法被稱為鎖機制,Hibernate提供的鎖分為兩種一種是樂觀鎖,另外一種是悲觀鎖。通過使用鎖能夠控制資料庫的並發性操作,限制使用者對資料庫的並發性的操作。
一、鎖簡介
鎖能控制資料庫的並行作業,通過使用鎖來控制資料庫的並行作業,Hibernate提供了兩種鎖來控制資料庫的操作,分別是樂觀鎖和悲觀鎖。
樂觀鎖:大多數是採用資料版本的方式實現,一般在資料庫中加入一個version欄位,在讀取資料的時候將version取出來,在儲存資料的時候判斷version的值是否小於資料庫中的version值,如果小於不會更新,否則可更新資料庫。
悲觀鎖:通常是由資料庫機制實現的,在整個過程中把資料鎖住(查詢時),只要事務不釋放(提交/復原)那麼任何使用者都不能查看和修改,通過使用LockMode來控制對資料庫的操作。
二、樂觀鎖
樂觀鎖是通過在資料庫中添加一個名為version的欄位來實現每次對資料的校正,在每次操作資料庫的時候會自動更新version的值,這樣每次操作的version值是不一樣的,所以如果有並行作業時將會首先校正version值是否小於資料庫的version值,如果小於的話不會更新資料庫,它的具體使用方法如下樣本:
2.1 映射實體Inventory.java
該類是映射的實體類,其中的屬性quantity是數位個數,在下面示範的樣本中通過操作quantity來更新資料庫的資訊,另外的version欄位是資料庫資訊的版本號碼,能通過校正來實現控制資料庫的操作,它的具體控制方法要在對應檔中編寫實現。
package com.src.hibernate;public class Inventory {//主鍵id,標識號private String itemNo;public String getItemNo() {return itemNo;}public void setItemNo(String itemNo) {this.itemNo = itemNo;}//列表名稱private String itemName;public String getItemName() {return itemName;}public void setItemName(String itemName) {this.itemName = itemName;}//個數private int quantity;public int getQuantity() {return quantity;}public void setQuantity(int quantity) {this.quantity = quantity;} //版本號碼 private int version; public int getVersion() { return version; } public void setVersion(int version) { this.version = version; } }
2.2 對應檔
在對應檔中需要添加<version>欄位來實現對資料庫版本號碼的映射,該標籤的name屬性要設定為對應實體中的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="com.src.hibernate.Inventory" table="t_inventory"><id name="itemNo"><generator class="assigned"/></id><version name="version"/><property name="itemName" type="string"/><property name="quantity" type="integer"/></class></hibernate-mapping>
2.3 資料操作
測試上面的樣本,在程式中添加了兩個方法來載入並修改資料,其中的testLoad1()方法首先載入資料庫中的資料,並修改欄位Quantity的值,修改後儲存資料,這時要設定斷點,在提交事務時設定斷點,停止對資料庫的提交操作,此時執行testLoad2()方法,將會發出錯誤資訊,不能更新資料庫操作。
public void testLoad1(){Session session=null;try{//建立session對象session=HibernateUtils.getSession();//開啟事務session.beginTransaction();Inventory inven=(Inventory)session.load(Inventory.class, "1001");System.out.println("p1-->name:"+inven.getItemName());inven.setQuantity(inven.getQuantity()-200);session.save(inven);session.getTransaction().commit();}catch(Exception e){e.printStackTrace();session.getTransaction().rollback();}finally{HibernateUtils.closeSession(session);}}public void testLoad2(){Session session=null;try{//建立session對象session=HibernateUtils.getSession();//開啟事務session.beginTransaction();Inventory inven=(Inventory)session.load(Inventory.class, "1001");System.out.println("p2-->name:"+inven.getItemName());inven.setQuantity(inven.getQuantity()-200);session.save(inven);session.getTransaction().commit();}catch(Exception e){e.printStackTrace();session.getTransaction().rollback();}finally{HibernateUtils.closeSession(session);}}
在不停止testLoad1()方法時執行testLoad2()方法,Hibernate發出如下語句,該語句說明testLoad2已經修改了資料庫中的資料,但是testLoad1也在修改只是沒有提交。
Hibernate: select inventory0_.itemNo as itemNo0_0_, inventory0_.version as version0_0_, inventory0_.itemName as itemName0_0_, inventory0_.quantity as quantity0_0_ from t_inventory inventory0_ where inventory0_.itemNo=?p2-->name:zhangsanHibernate: update t_inventory set version=?, itemName=?, quantity=? where itemNo=? and version=?
testLoad2()提交更改後,接下來運行testLoad1()的斷點,將會發出:
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) 的錯誤,說明兩個同時在修改同一條資料,導致了並行作業,發出了錯誤提示。
三、悲觀鎖
悲觀鎖和樂觀鎖不同,它是通過資料庫機制限制對資料庫的操作的,通過使用方法的LockMode參數來配置對資料庫的並行作業,在一次訪問過程中將會把資料鎖住(查詢時),只要事務不提交那麼任何使用者都不能查看和修改,其它使用者操作時將會被阻塞,不能同時操作,需要等待第一次訪問完成後再進行其它的操作。具體方法如下樣本。
3.1 實體Inventory.java
實體內容和樂觀鎖相同,不同的是在實體中不需要添加版本號碼屬性,因為它不是通過判斷版本號碼來限制操作的。
package com.src.hibernate;public class Inventory {//主鍵id,標識號private String itemNo;public String getItemNo() {return itemNo;}public void setItemNo(String itemNo) {this.itemNo = itemNo;}//列表名稱private String itemName;public String getItemName() {return itemName;}public void setItemName(String itemName) {this.itemName = itemName;}//個數private int quantity;public int getQuantity() {return quantity;}public void setQuantity(int quantity) {this.quantity = quantity;}}
3.2 對應檔
實體對應的對應檔也相當的簡單,只需要配置相應的映射即可,沒有其它複雜的操作,如下代碼:
<?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.src.hibernate.Inventory" table="t_inventory"><id name="itemNo"><generator class="assigned"/></id><property name="itemName" type="string"/><property name="quantity" type="integer"/></class></hibernate-mapping>
3.3 測試代碼
這裡採用兩個方法來測試操作,其中的testLoad1()方法首先查詢資料然後修改資料,testLoad2()也是查詢和修改資料,如下代碼:
public void testLoad1(){Session session=null;try{//建立session對象session=HibernateUtils.getSession();//開啟事務session.beginTransaction();Inventory inven=(Inventory)session.load(Inventory.class, "1001",LockMode.UPGRADE);System.out.println("p1-->name:"+inven.getItemName());inven.setQuantity(inven.getQuantity()-200);session.save(inven);session.getTransaction().commit();}catch(Exception e){e.printStackTrace();session.getTransaction().rollback();}finally{HibernateUtils.closeSession(session);}}public void testLoad2(){Session session=null;try{//建立session對象session=HibernateUtils.getSession();//開啟事務session.beginTransaction();Inventory inven=(Inventory)session.load(Inventory.class, "1001",LockMode.UPGRADE);System.out.println("p2-->name:"+inven.getItemName());inven.setQuantity(inven.getQuantity()-200);session.save(inven);session.getTransaction().commit();}catch(Exception e){e.printStackTrace();session.getTransaction().rollback();}finally{HibernateUtils.closeSession(session);}}
示範並行作業時在testLoad1()方法的列印處設定相應的斷點,然後啟動Debug調試,運行到斷點後停止,此時運行testLoad2()方法,實現了對資料庫的並行作業,在運行testLoad2()方法時Hibernate發出了SQL語句,但是卻一直不能查詢和檢索資料,因為資料庫中加了悲觀鎖,資料庫限制了並發性的操作,所以Hibernate只是發出了命令但是一直沒有操作許可權,也就是說testLoad2()方法在執行時遭到了阻塞,操作被壓到堆棧中等待上次的完成。當testLoad1()方法執行完後,將會從堆棧中繼續擷取testLoad2()的操作命令,這種樂觀鎖不僅限制了對資料的操作而且還能保證資料的完整性。
結語
Hibernate的鎖機制限制了資料庫的並發性的操作,功能很強大,在使用時建議使用悲觀鎖,因為悲觀鎖使用的是資料庫的機制,限制徹底,而且能確保資料的完整性。在使用這兩種鎖時各有優缺點,大資料量建議採用樂觀鎖,它會直接提示錯誤,效能上會高於悲觀鎖,悲觀鎖會加大資料庫的負擔,大資料量的話容易出現問題。