1. 程式人生 > >【hibernate框架】使用hibernate實現悲觀鎖和樂觀鎖

【hibernate框架】使用hibernate實現悲觀鎖和樂觀鎖

四種隔離機制不要忘記:(1,2,4,8)
1.read-uncommitted:能夠去讀那些沒有提交的資料(允許髒讀的存在)
2.read-committed:不會出現髒讀,因為只有另一個事務提交才會讀取來
結果,但仍然會出現不可重複讀和幻讀現象。
4.repeatable read:MySQL預設。可重複讀,讀資料讀出來之後給它加把鎖,
其他人先別更新,等我用完了你再更新。你的事務沒完,其他事務就不可能改這條記錄。
8.serializable:序列化,最高級別。一個一個來,不去併發。效率最低。

hibernate的隔離機制
i.hibernate.connection.isolation=2

ii.用悲觀鎖解決:repeatable read的問題(依賴於資料庫的鎖)
a)LockMode.None 無鎖的機制,Transaction結束時,切換到此模式
b)LockMode.read 在查詢的時候 hibernate會自動獲取鎖
c)LockMode.write insert update hibernate會自動獲取鎖
d)以上3中鎖的模式,是hibernate內部使用的
e)LockMode.UPGRADE_NOWAIT ORACLE支援的鎖的方式

例子:
Account.java:
package com.bjsxt.hibernate;


import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;


@Entity
public class Account {
private int id;
private int balance; //BigDecimal
@Id
@GeneratedValue
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}


}


hibernate.cfg.xml中配置:
<mapping class="com.bjsxt.hibernate.Account"/>

測試:
@Test
public void testSave() {
Session session = sf.openSession();
session.beginTransaction();

Account a = new Account();
a.setBalance(100);
session.save(a);

session.getTransaction().commit();
session.close();
}

@Test
public void testOperation1() {
Session session = sf.openSession();
session.beginTransaction();

Account a = (Account)session.load(Account.class, 1);
int balance = a.getBalance();
//do some caculations
balance = balance - 10;
//在儲存時很有可能會把在同一時期修改的給覆蓋掉
//這個時候上一把"鎖"就可以避免這個問題
a.setBalance(balance);
session.getTransaction().commit();
session.close();
}


//下面這個就是對上面那個例子做的修改
@Test
public void testPessimisticLock() {
Session session = sf.openSession();
session.beginTransaction();

//給它加把鎖,加鎖的機制上面已經提到了
Account a = (Account)session.load(Account.class, 1, LockMode.UPGRADE);
int balance = a.getBalance();
//do some caculation
balance = balance - 10;
a.setBalance(balance);
session.getTransaction().commit();
session.close();
}
這是依賴於資料庫的鎖的,也就是給資料庫一個指令,要求資料庫幫忙加鎖。
——————————————————————————————————————
iii.Hibernate(JPA)樂觀鎖定(ReadCommitted)
這不是依賴資料庫加鎖的,是在程式中加鎖的。
舉個例子:一個數據需要隔離機制(不能重複讀),這個時候在更新的欄位上加"版本號"(version欄位),一旦有人給它update一下,這個值就加1(version+1)。
那麼這種機制是如何產生隔離能力的呢?
原因是事務A讀取欄位的同時,事務B緊接著也讀取這個欄位,而且改了它,此時version變成1了。這個時候事務A就會檢查欄位是否被改變了,如果被改變它也做相應的改變,沒有改變就不改。


樂觀鎖的實現:(@Version)
Account.java:
package com.bjsxt.hibernate;


import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Version;


@Entity
public class Account {
private int id;
private int balance;
private int version;


@Version//加了這個註解就說明這個是專門用來做版本標註的
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
@Id
@GeneratedValue
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}


}


測試:
     
   @Test
public void testSave() {
Session session = sf.openSession();
session.beginTransaction();


Account a = new Account();
a.setBalance(100);
session.save(a);


session.getTransaction().commit();
session.close();
}


@Test
public void testOptimisticLock() {
Session session = sf.openSession();


Session session2 = sf.openSession();


session.beginTransaction();
Account a1 = (Account) session.load(Account.class, 1);

session2.beginTransaction();
Account a2 = (Account) session2.load(Account.class, 1);

a1.setBalance(900);
a2.setBalance(1100);


//第一個session一旦提交,version就會+1
session.getTransaction().commit();
System.out.println(a1.getVersion());


//第二個session提交的時候,一看version不一樣就會報錯
//出了錯誤做個記錄,下次再提交(也可以用其他方法)
                session2.getTransaction().commit();
System.out.println(a2.getVersion());


session.close();
session2.close();
}

——————————————————————————————————————


悲觀樂觀的區別:悲觀鎖認為一定會受到影響,我加鎖誰也別想動。

樂觀鎖,沒出事就好,出了事我再想辦法解決。