hibernate 事務(wù) annoation
Posted on 2009-08-31 10:12 瘋狂 閱讀(1243) 評(píng)論(0) 編輯 收藏 所屬分類: database 、hibernate
事務(wù)隔離 二 實(shí)際應(yīng)用中我們使用Read Committed作為我們的事務(wù)級(jí)別 也就是 已提交讀(Read Committed),因?yàn)槿绻脩?有權(quán)限 修改一個(gè) 數(shù)據(jù) ,那 那么 就可以提交這個(gè)事務(wù),這是系統(tǒng)需要解決的權(quán)限問題,由于這種情況會(huì)造成第二類數(shù)據(jù)丟失的情況,因此要配備樂觀鎖的機(jī)制,這種事物方法使用比較多。下面 舉例實(shí)驗(yàn): 1,例如A持久化后會(huì)被事務(wù)關(guān)聯(lián)(事務(wù)針對(duì)修改,插入,不針對(duì)查詢,如果對(duì)于一般不需要修改的表,如字典表,可以不配置事務(wù),而是配置二級(jí)緩存來提高性能) class A代碼(先不加樂觀鎖,會(huì)造成第二類數(shù)據(jù)丟失): hibernate配置:<property name="connection.isolation">2</property> 事先插入一條數(shù)據(jù)(a.setAnum(1);) 測試代碼: 打印信息: 可以看出他t1本來應(yīng)該是3但是被t2覆蓋 為此我們加上鎖: 修改A(鎖可以有兩種,version,和時(shí)間鎖,但是時(shí)間鎖有個(gè)精確度問題)使用version: 測試(同樣的測試代碼): 打印信息: 此時(shí)樂觀所就起作用了,防止了數(shù)據(jù)的丟失。針對(duì)異常可自行捕獲。
使用封鎖技術(shù),事務(wù)對(duì)申請(qǐng)的資源加鎖,但是會(huì)影響數(shù)據(jù)庫性能。根據(jù)數(shù)據(jù)對(duì)象封鎖的程度,可以分成多種不同的事務(wù)隔離級(jí)別。
數(shù)據(jù)并發(fā)執(zhí)行時(shí),產(chǎn)生不一致的現(xiàn)象:
1,丟失更新(Lost Update)
兩個(gè)事務(wù)讀入同一數(shù)據(jù)并修改,然后提交修改,T2 提交的結(jié)果破壞了 T1 提交的結(jié)果,導(dǎo)致 T1 的修改丟失。
2, 不可重復(fù)讀
事務(wù)T1 讀取數(shù)據(jù)后,事務(wù)T2 執(zhí)行了同一數(shù)據(jù)的跟新操作,使得事務(wù) T1 無法再現(xiàn)前一次讀取的結(jié)果。
事務(wù)1 讀取某一數(shù)據(jù)后,事務(wù)2 對(duì)該數(shù)據(jù)作了修改,事務(wù)1 再次讀取時(shí),得到數(shù)據(jù)和前一次不一致。
① 事務(wù)1 讀取某一些記錄后,事務(wù)2 刪除了同一數(shù)據(jù)源的部分?jǐn)?shù)據(jù),事務(wù)1 再次讀取時(shí),發(fā)現(xiàn)某些記錄丟失。
① 事務(wù)1 讀取某一些記錄后,事務(wù)2 插入了同一數(shù)據(jù)源的新數(shù)據(jù),事務(wù)1 再次讀取時(shí),發(fā)現(xiàn)某些記錄增加。
3, 讀“臟”數(shù)據(jù)
事務(wù)T1 修改某一數(shù)據(jù),并將其寫回物理數(shù)據(jù)庫。事務(wù)T2 讀取同一數(shù)據(jù)后,事務(wù)T1 由于某種原因被撤銷,數(shù)據(jù)庫將已經(jīng)修改的數(shù)據(jù)恢復(fù)原值,導(dǎo)致事務(wù)T2 保持的數(shù)據(jù)和數(shù)據(jù)庫中的數(shù)據(jù)產(chǎn)生了不一致。
ANSI SQL-99 標(biāo)準(zhǔn)定義了下列隔離級(jí)別:
Hibernate 在配置文件中聲明事務(wù)的隔離級(jí)別,Hibenate 獲取數(shù)據(jù)庫連接后,將根據(jù)隔離級(jí)別自動(dòng)設(shè)置數(shù)據(jù)庫連接為指定的事務(wù)隔離級(jí)別。
<property name="connection.isolation">8</property>
● 未提交讀(Read Uncommitted):隔離事務(wù)的最低級(jí)別,只能保證不會(huì)讀取到物理上損壞的數(shù)據(jù)。Hibernate配置:1;允許產(chǎn)生:1,2,3
● 已提交讀(Read Committed):常見數(shù)據(jù)庫引擎的默認(rèn)級(jí)別,保證一個(gè)事務(wù)不會(huì)讀取到另一個(gè)事務(wù)已修改但未提交的數(shù)據(jù)。Hibernate配置:2;允許產(chǎn)生:1,2
● 可重復(fù)讀(Repeatable Read):保證一個(gè)事務(wù)不能更新已經(jīng)由另一個(gè)事務(wù)讀取但是未提交的數(shù)據(jù)。相當(dāng)于應(yīng)用中的已提交讀和樂觀并發(fā)控制。Hibernate配置:4;允許產(chǎn)生:1
● 可串行化(Serializable):隔離事務(wù)的最高級(jí)別,事務(wù)之間完全隔離。系統(tǒng)開銷最大。Hibernate配置:8;這種情況很容易造成死鎖的問題,hibernate表現(xiàn)為:Deadlock found when trying to get lock; try restarting transaction
2-3、并發(fā)控制類型
根據(jù)使用的鎖定策略和隔離等級(jí),可以把事務(wù)的并發(fā)控制分為兩種:
① 悲觀并發(fā)控制
用戶使用時(shí)鎖定數(shù)據(jù)。主要應(yīng)用于數(shù)據(jù)爭用激烈的環(huán)境中,以及發(fā)生并發(fā)沖突時(shí)用鎖保護(hù)數(shù)據(jù)的成本低于回滾事務(wù)成本的環(huán)境中。
Hibernate 的悲觀鎖定不在內(nèi)存中鎖定數(shù)據(jù),由底層數(shù)據(jù)庫負(fù)責(zé)完成。
② 樂觀并發(fā)控制
用戶讀取數(shù)據(jù)時(shí)不鎖定數(shù)據(jù)。當(dāng)一個(gè)用戶更新數(shù)據(jù)時(shí),系統(tǒng)將進(jìn)行檢查該用戶讀取數(shù)據(jù)后其他用戶是否更改了該數(shù)據(jù),是則產(chǎn)生一個(gè)錯(cuò)誤,一般情況下,收到錯(cuò)誤信息的用戶將回滾事務(wù)并重新開始。主要用戶數(shù)據(jù)爭用不大,且偶爾回滾事務(wù)的成本低于讀取數(shù)據(jù)時(shí)鎖定數(shù)據(jù)的成本的環(huán)境中。
Hibernate 中使用元素 version 和 timestamp 實(shí)現(xiàn)樂觀并發(fā)控制模式的版本控制,并提供多種編程方式。版本是數(shù)據(jù)庫表中的一個(gè)字段,可以是一個(gè)遞增的整數(shù),也可以是一個(gè)時(shí)間戳,它們對(duì)應(yīng) Java 持久化類的一個(gè)屬性。事務(wù)提交成功后,Hibernate 自動(dòng)修改版本號(hào)。如果另外一個(gè)事務(wù)同時(shí)訪問同一數(shù)據(jù),若發(fā)現(xiàn)提交前的版本號(hào)和事前載入的版本號(hào)有出入,則認(rèn)為發(fā)生了沖突,事務(wù)停止執(zhí)行,撤銷操作,并拋出異常。應(yīng)用程序必須捕捉該異常并做出一定的處理。
⒈應(yīng)用程序級(jí)別的版本控制
⒉長生命周期會(huì)話的自動(dòng)化版本控制
⒊托管對(duì)象的自動(dòng)化版本控制
⒋定制自動(dòng)化版本控制
@Entity
public class A {
private int id;
private int anum;
private String aname;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
public int getId() {
return id;
}
//get..set
}
public static void test(){
new Thread(new Runnable(){//線程2,啟動(dòng)事務(wù)后不提交,然后讓線程1啟動(dòng)
public void run() {
Session session = HibernateSessionFactory.getSession();
Transaction tt = session.beginTransaction();
System.out.println("t2 statr....");
A a = (A) session.get(A.class, 1);
System.out.println("未修改之前的num:"+a.getAnum());
a.setAnum(a.getAnum()+1);
session.update(a);
try {
Thread.sleep(5000);//讓出時(shí)間讓線程1執(zhí)行
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
tt.commit();
session.clear();//清除一級(jí)緩存
System.out.println("t2 cummit end....");
A a1 = (A) session.get(A.class, 1);
System.out.println("修改之后的num:"+a1.getAnum());
session.close();
}
}).start();//正確結(jié)果為1+1=2
new Thread(new Runnable(){
public void run() {
Session session = HibernateSessionFactory.getSession();
Transaction tt = session.beginTransaction();
System.out.println("t1 statr....");
A a = (A) session.get(A.class, 1);
a.setAnum(a.getAnum()+2);
session.update(a);
tt.commit();
System.out.println("1 cummit end....");
session.clear();//清除一級(jí)緩存
session.close();
}
}).start();//正確結(jié)果為1+1=3
}
t2 statr....
Hibernate: select a0_.id as id0_0_, a0_.aname as aname0_0_, a0_.anum as anum0_0_ from A a0_ where a0_.id=?//讓出時(shí)間讓t1執(zhí)行
t1 statr....
Hibernate: select a0_.id as id0_0_, a0_.aname as aname0_0_, a0_.anum as anum0_0_ from A a0_ where a0_.id=?
未修改之前的num:1
Hibernate: update A set aname=?, anum=? where id=?
1 cummit end....
Hibernate: update A set aname=?, anum=? where id=?
t2 cummit end....
Hibernate: select a0_.id as id0_0_, a0_.aname as aname0_0_, a0_.anum as anum0_0_ from A a0_ where a0_.id=?
修改之后的num:2
private int version;
...
@Version//使用version注釋來表明版本控制
public int getVersion() {
return version;
}
t2 statr....
Hibernate: select a0_.id as id0_0_, a0_.aname as aname0_0_, a0_.anum as anum0_0_, a0_.version as version0_0_ from A a0_ where a0_.id=?
t1 statr....
Hibernate: select a0_.id as id0_0_, a0_.aname as aname0_0_, a0_.anum as anum0_0_, a0_.version as version0_0_ from A a0_ where a0_.id=?
未修改之前的num:1
Hibernate: update A set aname=?, anum=?, version=? where id=? and version=?
1 cummit end....
Hibernate: update A set aname=?, anum=?, version=? where id=? and version=?
Exception in thread "Thread-0" org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.eric.po.A#1]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1792)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2435)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2335)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2635)
at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:115)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:279)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:263)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:168)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:365)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137)
at com.eric.dao.Testcreate$1.run(Testcreate.java:139)
at java.lang.Thread.run(Thread.java:619)