最近在開發中對Spring中的事務標記@Transactional用的比較多,今天上網收集了一些內容,作一個簡單的總結~~~html
在service類前加上@Transactional,聲明這個service全部方法須要事務管理。每個業務方法開始時都會打開一個事務。java
Spring默認狀況下會對運行期例外(RunTimeException)進行事務回滾。這個例外是unchecked數據庫
若是遇到checked意外就不回滾。分佈式
如何改變默認規則:ide
1 讓checked例外也回滾:在整個方法前加上 @Transactional(rollbackFor=Exception.class)性能
2 讓unchecked例外不回滾: @Transactional(notRollbackFor=RunTimeException.class)測試
3 不須要事務管理的(只查詢的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)優化
若是在整個方法運行前就不會開啓事務
還能夠加上:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true),這樣就作成一個只讀事務,能夠提升效率。 ui
此外須要注意: 若是異常被try{}catch{}了,事務就不回滾了,若是想讓事務回滾必須再往外拋try{}catch{throw Exception}。spa
各類屬性的意義:
REQUIRED:業務方法須要在一個容器裏運行。若是方法運行時,已經處在一個事務中,那麼加入到這個事務,不然本身新建一個新的事務。
NOT_SUPPORTED:聲明方法不須要事務。若是方法沒有關聯到一個事務,容器不會爲他開啓事務,若是方法在一個事務中被調用,該事務會被掛起,調用結束後,原先的事務會恢復執行。
REQUIRESNEW:不論是否存在事務,該方法總彙爲本身發起一個新的事務。若是方法已經運行在一個事務中,則原有事務掛起,新的事務被建立。
MANDATORY:該方法只能在一個已經存在的事務中執行,業務方法不能發起本身的事務。若是在沒有事務的環境下被調用,容器拋出例外。
SUPPORTS:該方法在某個事務範圍內被調用,則方法成爲該事務的一部分。若是方法在該事務範圍外被調用,該方法就在沒有事務的環境下執行。
NEVER:該方法絕對不能在事務範圍內執行。若是在就拋例外。只有該方法沒有關聯到任何事務,才正常執行。
NESTED:若是一個活動的事務存在,則運行在一個嵌套的事務中。若是沒有活動事務,則按REQUIRED屬性執行。它使用了一個單獨的事務,這個事務擁有多個能夠回滾的保存點。內部事務的回滾不會對外部事務形成影響。它只對
DataSourceTransactionManager事務管理器起效。
// 若是有事務,那麼加入事務,沒有的話新建一個(不寫的狀況下)
@Transactional(propagation=Propagation.REQUIRED)
// 容器不爲這個方法開啓事務
@Transactional(propagation=Propagation.NOT_SUPPORTED)
// 不論是否存在事務,都建立一個新的事務,原來的掛起,新的執行完畢,繼續執行老的事務
@Transactional(propagation=Propagation.REQUIRES_NEW)
// 必須在一個已有的事務中執行,不然拋出異常
@Transactional(propagation=Propagation.MANDATORY)
// 必須在一個沒有的事務中執行,不然拋出異常(與Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.NEVER)
// 若是其餘bean調用這個方法,在其餘bean中聲明事務,那就用事務.若是其餘bean沒有聲明事務,那就不用事務.
@Transactional(propagation=Propagation.SUPPORTS)
@Transactional(propagation=Propagation.NESTED)
// readOnly=true只讀,不能更新,刪除
@Transactional (propagation = Propagation.REQUIRED,readOnly=true)
// 設置超時時間
@Transactional (propagation = Propagation.REQUIRED,timeout=30)
// 設置數據庫隔離級別
@Transactional (propagation = Propagation.REQUIRED,isolation=Isolation.DEFAULT)
@Transactional設置:
propagation:事務傳播性設置,Propagation枚舉類型。Spring支持的事務傳播屬性包括7種:
PROPAGATION_MANDATORY:方法必須在事務中執行,不然拋出異常。
PROPAGATION_NESTED:使方法運行在嵌套事務中,不然和PROPAGATION_REQUIRED同樣。
PROPAGATION_NEVER :當前方法永遠不在事務中運行,不然拋出異常。
PROPAGATION_NOT_SUPPORTED:定義爲當前事務不支持的方法,在該方法執行期間正在運行的事務會被暫停
PROPAGATION_REQUIRED:當前的方法必須運行在事務中,若是沒有事務就新建一個事務。新事務和方法一塊兒開始,隨着方法返回或者拋出異常時終止。
PROPAGATION_REQUIRED_NEW :當前方法必須新建一個事務,若是當前的事務正在運行則暫停。
PROPAGATION_SUPPORTS :規定當前方法支持當前事務,可是若是沒有事務在運行就使用非事務方法執行。
isolation:事務隔離性級別設置,Isolation枚舉類型
ISOLATION_DEFAULT :使用數據庫默認的隔離級別
ISOLATION_COMMITTED:容許其餘事務已經提交的更新(防止髒讀取)
ISOLATION_READ_UNCOMMITTED:容許讀取其餘事務未提交的更新,會致使三個缺陷發生。執行速度最快
ISOLATION_REPEATABLE_READ :除非事務自身更改了數據,不然事務屢次讀取的數據相同(防止髒數據,屢次重複讀取)
ISOLATION_SERIALIZABLE:隔離級別最高,能夠防止三個缺陷,可是速度最慢,影響性能。
readOnly:讀寫性事務,只讀性事務,布爾型
對數據庫的操做中,查詢是使用最頻繁的操做,每次執行查詢時都要從數據庫中從新讀取數據,有時屢次讀取的數據都是相同的,這樣的數據操做不只浪費了系統資源,還影響了系統速度。對訪問量大的程序來講,節省這部分資源能夠大大提 升系統速度。
將事務聲明爲只讀的,那麼數據庫能夠根據事務的特性優化事務的讀取操做
timeout:超時時間,單位秒
事務可能由於某種緣由很長時間沒有反應,這期間可能鎖定了數據庫表,影響性能。設置超時時間,若是超過該時間,事務自動回滾。
rollbackFor:一組異常類的實例,遇到時必須進行回滾
rollbackForClassname:一組異常類的名字,遇到時必須進行回滾
noRollbackFor:一組異常類的實例,遇到時必須不回滾
noRollbackForClassname:一組異常類的名字,遇到時必須不回滾
實例一:事務的回滾狀況
一、默認狀況對非運行時異常不進行回滾操做
/** * 運行時異常默認事務回滾 * @throws Exception */ @Transactional public void updateUser_1() throws Exception { Map<String,String> userMap=new HashMap<String,String>(); userMap.put("id","1"); userMap.put("userName","測試_01"); userMap.put("password","654321"); userDao.updateUserByID(userMap); throw new RuntimeException(); //拋出運行時異常 默認狀況下回滾 事務---------------------->回滾 }
二、非運行時異常默認事務不回滾
/** * 非運行時異常默認事務不回滾 * @throws Exception */ @Transactional public void updateUser_3() throws Exception { Map<String,String> userMap=new HashMap<String,String>(); userMap.put("id","1"); userMap.put("userName","測試_01"); userMap.put("password","654321"); userDao.updateUserByID(userMap); throw new Exception(); //拋出非運行時異常 默認狀況下不回滾 事務---------------------->不回滾 }
三、對全部異常事務都進行回滾操做
/** * 對全部異常事務都進行回滾操做 * @throws Exception */ @Transactional(rollbackFor=Exception.class) public void updateUser_6() throws Exception { Map<String,String> userMap=new HashMap<String,String>(); userMap.put("id","1"); userMap.put("userName","測試_01"); userMap.put("password","654321"); userDao.updateUserByID(userMap); throw new Exception(); //對全部異常事務都進行回滾操做 事務---------------------->回滾 }
4、對全部異常事務都不進行回滾操做
/** * 對全部異常事務都不進行回滾操做 * @throws Exception */ @Transactional(noRollbackFor=Exception.class) public void updateUser_7() throws Exception { Map<String,String> userMap=new HashMap<String,String>(); userMap.put("id","1"); userMap.put("userName","測試_01"); userMap.put("password","654321"); userDao.updateUserByID(userMap); throw new RuntimeException(); //對全部異常,事務都不進行回滾操做 事務---------------------->不回滾 }
實例二:對try{} catch{}的異常不回滾
/** * 對於try{} catch{}的異常不進行回滾 * @throws Exception */ @Transactional public void updateUser_5() throws Exception { Map<String,String> userMap=new HashMap<String,String>(); userMap.put("id","1"); userMap.put("userName","測試_01"); userMap.put("password","654321"); userDao.updateUserByID(userMap); try{ throw new RuntimeException(); //運行時異常被捕獲,事務不回滾 } catch(Exception e){ System.err.println("運行時異常被捕獲,事務不回滾"); } }
實例三:關於嵌套事務的回滾原則
說實話對於嵌套事務的測試,我如今仍然有一些疑問,好比說若是內部事務設置爲
// 不論是否存在事務,都建立一個新的事務,原來的掛起,新的執行完畢,繼續執行老的事務
@Transactional(propagation=Propagation.REQUIRES_NEW)
那這樣的狀況下,內部事務設置爲Propagation.REQUIRES_NEW,外部事務設置爲Propagation.REQUIRED,那麼若是在內部事務執行完後以後,若是外部事務拋出了運行時異常,那麼對於內部事務而言
因爲是新開的一個單獨的事務,這樣的狀況下內部事務應該不受外部事務的影響而回退,可是事實上通過測試,內部事務也是回滾的。感興趣的同窗能夠一塊兒探討啊~
在網上找了一些博客,下面貼出一篇比較好嵌套事務的博客:
http://blog.chinaunix.net/uid-10289334-id-2964925.html
一、外部事務拋異常,內部事務回滾(通常來講,只要外部開啓事務,內部方法不管是否開啓事務,只要出現相應的運行時異常,事務都將進行回滾操做)
/** * 測試事務的相關信息 * @throws Exception */ @Override @Transactional(propagation=Propagation.REQUIRES_NEW) public void updateUser() throws Exception { Map<String,String> userMap=new HashMap<String,String>(); userMap.put("id","1"); userMap.put("userName","測試_01"); userMap.put("password","654321"); userDao.updateUserByID(userMap); insideMethod_01(); //1 、異常 不捕獲 事務---------------------->回滾 // insideMethod_02(); //二、異常 捕獲 事務---------------------->不回滾 throw new RuntimeException(); //三、測試外部方法拋異常 事務---------------------->回滾 // throw new Exception();//四、默認狀況,非運行時異 常 事務---------------------->不回滾 } /** * 事務測試的內部方法 內部方法拋運行時異常不捕獲 </br> * * 事務會對內外方法進行回滾操做 */ @Override @Transactional(propagation=Propagation.REQUIRES_NEW) public void insideMethod_01(){ Map<String,String> userMap=new HashMap<String,String>(); userMap.put("id","2"); userMap.put("id","2"); userMap.put("userName","測試_02"); userMap.put("password","654321"); userDao.updateUserByID(userMap); // int value=1/0;//運行時異常 }
二、內部事務拋異常,外部事務回滾(通常來講,只要外部開啓事務,內部方法不管是否開啓事務如何配置,只要出現相應的運行時異常,內外事務都將進行回滾操做)
/** * 測試事務的相關信息 * @throws Exception */ @Override @Transactional(propagation=Propagation.REQUIRES_NEW) public void updateUser() throws Exception { Map<String,String> userMap=new HashMap<String,String>(); userMap.put("id","1"); userMap.put("userName","測試_01"); userMap.put("password","654321"); userDao.updateUserByID(userMap); insideMethod_01(); //1 、異常 不捕獲 事務---------------------->回滾 // insideMethod_02(); //二、異常 捕獲 事務---------------------->不回滾 // throw new RuntimeException(); //三、測試外部方法拋異常 事務---------------------->回滾 // throw new Exception();//四、默認狀況,非運行時異 常 事務---------------------->不回滾 } /** * 事務測試的內部方法 內部方法拋運行時異常不捕獲 </br> * * 事務會對內外方法進行回滾操做 */ @Override @Transactional(propagation=Propagation.REQUIRES_NEW) public void insideMethod_01(){ Map<String,String> userMap=new HashMap<String,String>(); userMap.put("id","2"); userMap.put("id","2"); userMap.put("userName","測試_02"); userMap.put("password","654321"); userDao.updateUserByID(userMap); int value=1/0;//運行時異常 }
注:這裏還有一點在實際開發中須要注意,若是外部方法調用了遠程的接口,因爲它們不在一個事務控制中,相似於分佈式事務,這種狀況遠程事務是沒法進行回滾操做的~