你們好,我是肥朝。前陣子看到這麼一條粉絲的朋友圈php
AQS到底有什麼用?難道就真的只是爲了面試嗎?面試
固然不是說AQS沒用,若是你不是作基礎架構或者中間件開發,你很難感覺到AQS的威力。固然,學習不少時候,須要的是正向反饋,學了太多造火箭的東西,面試完就再也用不上,天然很難有動力保持持續學習。那麼,有沒有受衆羣體大,就算平時CRUD的同窗也用獲得,同時面試又喜歡問,最重要的是,出問題的時候,搜索還不容易搜索出答案的知識點?(肥朝的老粉絲都知道,這最後一點對於肥朝來講很是重要)spring
那麼,這個就是肥朝以前提到的Spring事務傳播機制
。數據庫
緣由很簡單,由於能知足上述三個條件的,肥朝第一個想到的,就是Spring事務傳播機制,他具有了幾個條件微信
1.CRUD的同窗,平時和事務打交道最多。而且,事務一旦出了問題,那但是爆炸性的傷害,這點毋容置疑架構
2.面試高頻考點,通常還會問數據庫的隔離級別
。可是肥朝發現,不少同窗把數據庫的隔離級別
和Spring的事務傳播機制
這兩個概念搞混,其實這是兩碼事。同時,也有比較出名的面試題,"作51次操做,前面50次成功,第51次失敗,如何把前面成功的50次提交,第51次失敗的回滾"。若是你對Spring的事務傳播機制不瞭解,那麼你對於這個問題,是沒有什麼頭緒的,緣由在於,不少同窗平時只處理過,所有提交和所有回滾兩個狀況。app
3.即便你在別的地方看過相似的Spring事務傳播機制的文章,對於常規的狀況是沒問題,可是若是在多個try模型下,你對因而否能回滾,內心仍是沒底的,說白了,就是你仍是沒能摸透原理!ide
4.最重要的固然是肥朝以前答應過你們要寫這篇,不想作渣男。單元測試
Spring的事務傳播機制有如下七種學習
PROPAGATION_REQUIRED
:Spring的默認傳播級別,若是上下文中存在事務則加入當前事務,若是不存在事務則新建事務執行。
PROPAGATION_SUPPORTS
:若是上下文中存在事務則加入當前事務,若是沒有事務則以非事務方式執行。
PROPAGATION_MANDATORY
:該傳播級別要求上下文中必須存在事務,不然拋出異常。
PROPAGATION_REQUIRES_NEW
:該傳播級別每次執行都會建立新事務,並同時將上下文中的事務掛起,執行完當前線程後再恢復上下文中事務。(子事務的執行結果不影響父事務的執行和回滾)
PROPAGATION_NOT_SUPPORTED
:當上下文中有事務則掛起當前事務,執行完當前邏輯後再恢復上下文事務。(下降事務大小,將非核心的執行邏輯包裹執行。)
PROPAGATION_NEVER
:該傳播級別要求上下文中不能存在事務,不然拋出異常。
PROPAGATION_NESTED
:嵌套事務,若是上下文中存在事務則嵌套執行,若是不存在則新建事務。(save point概念)
看完這七種是否是雲裏霧裏?那就對了!坦白說,這七種你記得住,其實也沒多大意義,記得住多半也是背下來的。其中PROPAGATION_REQUIRED
這種默認的狀況,是咱們用得最多的,基本覆蓋90%的狀況,也就是你們常見的,一塊兒回滾的狀況。還有一個是PROPAGATION_REQUIRES_NEW
。基本你把這兩個掌握了,應對95%的狀況一點兒問題都沒有,若是還有問題,你再來查這七種,找到你合適的。
若是隻是羅列概念,那意義不大,所以咱們採用應試教育的方式來作題,纔是檢驗掌握程度比較好的作法
這種也是你們最多見的狀況
@Transactional
@Override
public void Example1(User user) {
userMapper.insert(user);
propagationService.required();
}
@Transactional
@Override
public void required() {
throw new NullPointerException("肥朝僞裝拋出了異常");
}
單元測試
@Test
public void testExample1() throws Exception {
User user = new User();
user.setName("微信公衆號:肥朝");
userService.Example1(user);
}
開始敲黑板劃重點了,這個狀況也是你們最多見的狀況之一,可是因爲這個try起來了,那麼,這個insert可否插入數據呢?
@Transactional
@Override
public void Example2(User user) {
userMapper.insert(user);
try {
propagationService.required();
} catch (Exception e) {
e.printStackTrace();
}
}
@Transactional
@Override
public void required() {
throw new NullPointerException("肥朝僞裝拋出了異常");
}
單元測試
@Test
public void testExample2() throws Exception {
User user = new User();
user.setName("微信公衆號:肥朝");
userService.Example2(user);
}
這個案例和上面的案例很類似,區別在於,這裏的隔離級別是Propagation.REQUIRES_NEW
,那麼,這個insert可否插入數據呢?
@Transactional
@Override
public void Example3(User user) {
userMapper.insert(user);
try {
propagationService.requiresNew();
} catch (Exception e) {
e.printStackTrace();
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void requiresNew() {
throw new NullPointerException("肥朝僞裝拋出了異常");
}
單元測試
@Test
public void testExample3() throws Exception {
User user = new User();
user.setName("微信公衆號:肥朝");
userService.Example3(user);
}
這個和案例一很想,結果會有差異嗎?最後insert能插入嗎?
@Transactional
@Override
public void Example4(User user) {
userMapper.insert(user);
propagationService.requiresNew();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void requiresNew() {
throw new NullPointerException("肥朝僞裝拋出了異常");
}
單元測試
@Test
public void testExample4() throws Exception {
User user = new User();
user.setName("微信公衆號:肥朝");
userService.Example4(user);
}
這個不用說,稍微有點Java常識的人都知道,異常必然會致使回滾,數據庫不會插入數據。
這個到底會不會插入數據呢?畢竟這個異常被try起來了。這個時候,正常的思惟都會認爲,能正常插入數據,可是答案是,不會插入數據,而且拋出異常
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
爲啥會這樣呢?
這個和案例二很像,由於有了案例二的陰影,這個時候你就變得不肯定了。答案是,能正常插入數據。
這個和案例一很像,原本你是很肯定能不能插入數據的,可是有了案例三的陰影以後,這個時候你又變得不肯定了。答案是,不會插入數據。
這四個案例,通過肥朝精心挑選,只要你把這四個案例和原理弄熟,再複雜的各類try模型下,事務是否回滾,你都清清楚楚。因此你覺得叫你關注肥朝的公衆號是害你嗎?是愛你啊老哥!
咱們先來看看@Transactional
的核心方法
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// 開啓事務
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// 執行業務方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 回滾事務
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
// 提交事務
commitTransactionAfterReturning(txInfo);
return retVal;
}
咱們重點解析案例二和案例三的狀況。咱們再把那兩個事務傳播機制的意思來解讀一下:
PROPAGATION_REQUIRED
:Spring的默認傳播級別,若是上下文中存在事務則加入當前事務,若是不存在事務則新建事務執行。
PROPAGATION_REQUIRES_NEW
:該傳播級別每次執行都會建立新事務,並同時將上下文中的事務掛起,執行完當前線程後再恢復上下文中事務。
首先來看案例二,執行Example2
方法的時候,由上文得知,將開啓一個事務,再執行到required
方法時,此時,由於用得的默認的隔離級別,所以,這個時候,會加入到剛纔的事務之中,而後required
方法中,出現了異常,咱們來看
completeTransactionAfterThrowing(txInfo, ex);
中的核心方法
從doSetRollbackOnly(status)
這個單詞就知道,required
的時候,已經把這個事務設置成RollbackOnly
,所以,雖然try住了,可是Example2
執行完提交的時候,卻發現沒法提交,因此異常信息以下:
Transaction rolled back because it has been marked as rollback-only
一圖勝千言,我用一張圖來描述這個關係
那爲啥案例三,又能插入數據呢?仍是用一張圖來描述
@Transactional
有不少注意點
在同個類中A方法調用B方法,B方法是不會開啓事務,天然也就不會用到事務的傳播機制。這個原理後續肥朝會解析,固然若是你連這句話都不知道是什麼意思,假粉實錘了!
@Transactional默認狀況下,只回滾RuntimeException
。若是你拋出的異常不是RuntimeException
,可能致使在默認狀況下和本文有所誤差
固然,這麼多注意點,哪記得住,所以,留意後續的原理解析,就很是有必要了。
什麼?你也想親自體驗一下從原理源碼解決問題的過程?沒問題,肥朝已經把demo給你準備好了,添加肥朝微信好友(微信號:feizai_php),【無套路】分享地址告訴你。