1、悲觀鎖
它指的是對數據被外界修改保持保守態度,因些,在整個數據處理過程中,將數據牌鎖定狀態。悲觀鎖的實現,往往依靠數據庫提供的鎖機制(也只有數據庫層的鎖機制才能保證數據訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會修改數據)。
一個典型的悲觀鎖調用示例:
select * from account where name = "12345" for update
通過for update子句,這條SQL鎖定了account表中所有符合檢索條件的記錄。本次事務提交之前(事務提交時會釋放事務過程中的鎖),外界無法修改這些記錄。
2、樂觀鎖
相對悲觀鎖而言,樂觀鎖機制采取了更加寬松的加鎖機制。悲觀鎖大多數情況下依靠數據庫的鎖機制,以操作最大程度的獨占性。但隨着而來的就是數據庫性能的大量開銷,特別是對長事務而言,這樣的開銷往往無法承受。
樂觀鎖,大多是基於數據版本(Version)記錄機制實現。何謂數據版本?即為數據增加一個版本標識,在基於庫表的版本解決方案中,一般是通過為數據庫表增加version字段來實現。
讀取出數據時,將此版本號一同讀出,之生更新時,對此版本號加1.此時,將提交數據的版本數據與數據庫表對應記錄的當前版本信息進行比對,如果提交的數據版本號大於數據庫表當前版本號,則予以更新,否則認為是過期數據。
例如,兩個人同時在同一個帳號取錢,賬號有100,A取50,B取20,A先提交,B的余額即為80,這時就不同步了,A提交后版本已經變了2了,而B看到的還是1的版本,此時B的提交必須被駁回。
需要注意的是,樂觀鎖機制往往基於系統中的數據存儲邏輯,因此也具備一定的局限性。如有些例子,由於樂觀鎖機制是在我們的系統中實現,來自外部系統的用戶余額更新操作不受我們系統控制,因此可能會造成非法數據被更新到數據庫中。
在系統設計階段,我們應該充分考慮到某些情況出現的可能性,並進行相應調整(如將樂觀鎖策略在數據庫存儲過程中實現,對外只開放基於此存儲過程的數據更新途徑,而不是將數據庫表直接對外公開)。
Hibernate 在其數據訪問引擎中內置了樂觀鎖實現。如果不用考慮外部系統對數據庫的更新操作,利用Hibernate提供的透明化樂觀鎖實現,將大大提升我們的生產力。
(1)、實體類
package learn.hibernate.bean; import java.util.Date; import java.util.HashSet; import java.util.Set; /** * 持久化類設計 * 注意: * 持久化類通常建議要有一個持久化標識符(ID) * 持久化標識符通常建議使用封裝類(例如:Integer 因為基本類型存在默認值) * 持久化類通常建議手動添加一個無參構造函數 (因為有些操作是通過放射機制進行的) * 屬性通常建議提供 getter/setter 方法 * 持久化類不能使用 final 修飾 * 持久化類中如果使用了集合類型數據,只能使用集合所對應的接口類型來聲明(List/Map/Set) * 如下:ArrayList list = new ArrayList(); 不行 * List list = new ArrayList(); 可行 */ public class Person { private Integer id; private String name; private int age; private int passwork; private Date birthday; private Integer version; public Person() { } public Person(String name, int age, int passwork, Date birthday) { super(); this.name = name; this.age = age; this.passwork = passwork; this.birthday = birthday; } @Override public String toString() { return "Person [id=" + id + ", name=" + name + ", age=" + age + ", passwork=" + passwork + ", birthday=" + birthday + "]"; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getPasswork() { return passwork; } public void setPasswork(int passwork) { this.passwork = passwork; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public Integer getVersion() { return version; } public void setVersion(Integer version) { this.version = version; } }(2)、持久化映射文件
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="learn.hibernate.bean"> <!-- optimistic-lock="version" 指定持久化類的樂觀鎖策略 --> <class name="Person" table="t_person" optimistic-lock="version"> <id name="id" column="person_id"> <generator class="native"/> </id> <!-- 配置 鎖字段和對應的屬性關聯,以及字段類型 --> <version name="version" column="t_version" type="integer"/> <property name="name" column="t_name"/> <property name="age"/> <property name="passwork"/> <property name="birthday"/> </class> </hibernate-mapping>(3)、測試類
/** * hibernate 在獲取數據的時候返回一個鎖狀態 * 在提交數據的時候會自動的將鎖狀態進行改變 */ @Test public void testUpdate(){ Person p1 = (Person)s1.get(Person.class, 1); Person p2 = (Person)s2.get(Person.class, 1); System.out.println("----------1---start-----------"); tx = s1.beginTransaction(); p1.setName("p1"); s1.update(p1); tx.commit(); System.out.println("----------1---end-----------"); System.out.println("----------2---start-----------"); tx = s2.beginTransaction(); p2.setName("p2"); s2.update(p2); tx.commit(); System.out.println("----------2---end-----------"); }
