1.定義
事務的提交是指事務里的所有操作都正常完成。
事務的回滾是指程序或數據處理錯誤,將程序或數據恢復到上一次正確狀態的行為。
2.代碼中事務控制的3種方式
-
編程式事務:就是直接在代碼里手動開啟事務,手動提交,手動回滾。對於編程式事務管理,spring推薦使用TransactionTemplate。
-
聲明式事務:就是使用SpringAop配置事務,這種方式大大的簡化了編碼,需要注意的是配置切入點表達式一定要寫正確。聲明式事務管理也有兩種常用的方式,一種是基於tx和aop名字空間的xml配置文件,另一種就是基於@Transactional注解。
-
注解事務:直接在Service層的方法上面加上@Transactional注解,個人比較喜歡用這種方式。
3.編程式事務和聲明式事務對比
顯然聲明式事務管理要優於編程式事務管理,這正是spring倡導的非侵入式的開發方式。聲明式事務管理使業務代碼不受污染,一個普通的POJO對象,只要加上注解就可以獲得完全的事務支持。和編程式事務相比,聲明式事務唯一不足地方是,后者的最細粒度只能作用到方法級別,無法做到像編程式事務那樣可以作用到代碼塊級別。但是即便有這樣的需求,也存在很多變通的方法,比如,可以將需要進行事務管理的代碼塊獨立為方法等等。
4.事務不回滾的原因
在工作中,看過別人寫的代碼出現了事務不回滾的現象。當然,事務不回滾的都是采用的聲明式事務或者是注解事務;編程式事務都是自己寫代碼手動回滾的,因此是不會出現不回滾的現象。
再說下聲明式事務和注解事務回滾的原理:當被切面切中或者是加了注解的方法中拋出了RuntimeException異常時,Spring會進行事務回滾。默認情況下是捕獲到方法的RuntimeException異常,也就是說拋出只要屬於運行時的異常(即RuntimeException及其子類)都能回滾;但當拋出一個不屬於運行時異常時,事務是不會回滾的。
下面說說我經常見到的3種事務不回滾的產生原因:
-
(1)聲明式事務配置切入點表達式寫錯了,沒切中Service中的方法
-
(2)Service方法中,把異常給try catch了,但catch里面只是打印了異常信息,沒有手動拋出RuntimeException異常
-
(3)Service方法中,拋出的異常不屬於運行時異常(如IO異常),因為Spring默認情況下是捕獲到運行時異常就回滾
5.如何保證事務回滾
正常情況下,按照正確的編碼是不會出現事務回滾失敗的。下面說幾點保證事務能回滾的方法
-
(1)如果采用聲明式事務,一定要確保切入點表達式書寫正確
-
(2)如果Service層會拋出不屬於運行時異常也要能回滾,那么可以將Spring默認的回滾時的異常修改為Exception,這樣就可以保證碰到什么異常都可以回滾。具體的設置方式也說下:
① 聲明式事務,在配置里面添加一個rollback-for,代碼如下
<tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
② 注解事務,直接在注解上面指定,代碼如下
@Transactional(rollbackFor=Exception.class)
-
(3)只有非只讀事務才能回滾的,只讀事務是不會回滾的
-
(4)如果在Service層用了try catch,在catch里面再拋出一個 RuntimeException異常,這樣出了異常才會回滾
-
(5)如果你不喜歡(4)的方式,你還可以直接在catch后面寫一句回滾代碼(TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();)來實現回滾,這樣的話,就可以在拋異常后也能return 返回值;比較適合需要拿到Service層的返回值的場景。具體的用法可以參見考下面的偽代碼
/** TransactionAspectSupport手動回滾事務:*/ @Transactional(rollbackFor = { Exception.class }) public boolean test() { try { doDbSomeThing(); } catch (Exception e) { e.printStackTrace(); //就是這一句了, 加上之后拋了異常就能回滾(有這句代碼就不需要再手動拋出運行時異常了)
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); return false;