? ? ? ? 相信大家都是程序猿中的大牛,但是有些小細節(jié)總會被遺忘,但是呢,到了面試時又不得不拿出來記一下,然后應對面試,畢竟嘛,能成為面試官,我覺得都是待時間長業(yè)務熟悉透了升級,真正技術過硬,也沒必要花時間來面試小嘍啰了,對吧?所以大部分面試問的問題都差不多,Spring事務管理也恰好在里面了。
一、認識一下事務
? ? ? ? 事務這位老哥就是transaction,就是人們需要代碼完成的一些增刪改查,可以丟給這位老哥代替你來完成,不用你操心。Spring事務機制就是基于數(shù)據(jù)庫那四個事務特性而設計開發(fā)出來的,所以我們可以先入手了解事務四大特性嘛,對吧。事務四大特性(ACID)分別為:Atomic(原子性)、Consistency(一致性)、Isolation(隔離性)和Durability(持久性)的英文縮寫。
????????①原子性:原子性就是說一個事務里全部事情要么成功了,要么就失敗了,不會在中間環(huán)節(jié)結束了。事務在執(zhí)行期間如果發(fā)生了錯誤,就會回滾到事務發(fā)生前的狀態(tài),就好像事務從來沒發(fā)生過似的。
????????②一致性:事務的一致性指的是在一個事務執(zhí)行之前和執(zhí)行之后數(shù)據(jù)庫都必須處于一致性狀態(tài)。如果事務成功地完成,那么系統(tǒng)中所有變化將正確地應用,系統(tǒng)處于有效狀態(tài)。如果在事務中出現(xiàn)錯誤,那么系統(tǒng)中的所有變化將自動地回滾,系統(tǒng)返回到原始狀態(tài)。舉個例子就是:A和B各有5000元,A給B轉賬1000元,成功轉賬后,A+B的總額還是10000元。
????????③隔離性:每個事務執(zhí)行都各自被隔離開,不相互影響各自的執(zhí)行過程。也就是在并發(fā)環(huán)境中,當不同的事務同時操縱相同的數(shù)據(jù)時,每個事務都有各自的完整數(shù)據(jù)空間。由并發(fā)事務所做的修改必須與任何其他并發(fā)事務所做的修改隔離。事務查看數(shù)據(jù)更新時,數(shù)據(jù)所處的狀態(tài)要么是另一事務修改它之前的狀態(tài),要么是另一事務修改它之后的狀態(tài),事務不會查看到中間狀態(tài)的數(shù)據(jù)。
????????④持久性:一旦事務完成,無論發(fā)生什么系統(tǒng)錯誤,它的結果都不應該受到影響,這樣就能從任何系統(tǒng)崩潰中恢復過來。通常情況下,事務的結果被寫到持久化存儲器中。
二、Spring事務的實現(xiàn)方式
? ??????Spring支持編程式事務管理以及聲明式事務管理兩種方式。
? ? ? ? 1、編程式事務管理
? ??????編程式事務管理是侵入性事務管理,編程式事務管理對基于 POJO 的應用來說是唯一選擇。我們需要在代碼中調用beginTransaction()、commit()、rollback()等事務管理相關的方法,這就是編程式事務管理。對于編程式事務管理,Spring推薦使用TransactionTemplate。
? ? ? ? 2、聲明式事務管理
? ??????聲明式事務管理屬于spring事務管理的非侵入性事務管理的一種,建立在AOP之上,其本質是對方法前后進行攔截,然后在目標方法開始之前創(chuàng)建或者加入一個事務,執(zhí)行完目標方法之后根據(jù)執(zhí)行的情況提交或者回滾。與編程式事務管理最大的區(qū)別就是,編程式事務管理是代碼塊實現(xiàn)的,不適用于業(yè)務量大的場景下,因為每個業(yè)務邏輯不同,會需要編寫不同邏輯的事務管理代碼,而聲明式事務管理是屬于方法級別的事務管理,只需要把事務邏輯配置在配置文件里,spring的會通過IOC容器反向控制把相關邏輯依賴注入應用到業(yè)務邏輯里。常用的方法有三種:①基于 TransactionProxyFactoryBean的聲明式事務管理,②基于 @Transactional 的聲明式事務管理,③基于Aspectj AOP配置事務。
? ? ? ? ①TransactionProxyFactoryBean的聲明式事務管理

? ?????②基于 @Transactional 的聲明式事務管理?


????????可以看出,使用@Transactional注解的方式配置文件要簡單的多,將事務交給事務注解驅動。它有個缺陷是他會把所有的連接點都作為切點將事務織入進去,顯然只需要在buyStock()方法織入事務即可。下面看看最后一種實現(xiàn),它就可以精準的織入到指定的連接點
? ??????知識點:
????????事務超時:@Transactional(timeout?=?60)
????????如果用這個注解描述一個方法的話,線程已經(jīng)跑到方法里面,如果已經(jīng)過去60秒了還沒跑完這個方法并且線程在這個方法中的后面還有涉及到對數(shù)據(jù)庫的增刪改查操作時會報事務超時錯誤(會回滾)。
如果已經(jīng)過去60秒了還沒跑完但是后面已經(jīng)沒有涉及到對數(shù)據(jù)庫的增刪改查操作,那么這時不會報事務超時錯誤(不會回滾)。
回滾:
Spring管理事務默認回滾的異常是什么?
答案是?RuntimeException或者Error。
????????注意:如果事務在try{}catch(Exception?e){e.printStackTrace();}中跑,并且catch中只是打印e的話,那么事務不會rollback。因為異常被catch掉了,框架不知道發(fā)生了異常。
如果想要rollback,可以加上rollbackFor=Exception.class,然后:
? ? ? ?(1)在方法上添加?throws??Exception,將方法中出現(xiàn)的異常拋出給spring事務,
?。?)去掉方法體中的try?catch
(3)catch?(Exception?e)?{??throw?e;}繼續(xù)向上拋,目的是讓spring事務捕獲這個異常。
rollbackFor=Exception.class,catch(){
? ? throw new RunTimeException();
}
????????如果不加rollbackFor=Exception.class,拋出newException() 是不會回滾的。Spring源碼如下:
publicbooleanrollbackOn(Throwable?ex)?{
? ??????????????????return(exinstanceofRuntimeException?||?exinstanceofError);
}?
????????如果是RuntimeException或Error的話,就返回True,表示要回滾,否則返回False,表示不回滾。
????????只有spring事務捕獲到Exception異常后,@Transactional(rollbackFor=Exception.class),才會起到應有的作用;catch?(Exception?e)?{????????????e.printStackTrace();????????}這句是捕獲try中出現(xiàn)的Exception然后將異常信息打印出來,僅僅是打印出來,然后什么也沒干。
? ? ? ? ? @Transactional(timeout?= 60,rollbackFor=Exception.class)與rollbackFor=Exception.class的作用是
????????(1)讓checked例外也回滾:在整個方法前加上?@Transactional(rollbackFor=Exception.class)
????????(2)讓unchecked例外不回滾:?@Transactional(notRollbackFor=RunTimeException.class)
checked?Unchecked exception是運行時錯誤。
? ??????③基于Aspectj AOP配置事務

三、Spring事務傳播機制
????????事務的傳播性一般用在事務嵌套的場景,比如一個事務方法里面調用了另外一個事務方法,那么兩個方法是各自作為獨立的方法提交還是內層的事務合并到外層的事務一起提交,這就是需要事務傳播機制的配置來確定怎么樣執(zhí)行。
????????常用的事務傳播機制如下:
(1)PROPAGATION_REQUIRED
????????Spring默認的傳播機制,能滿足絕大部分業(yè)務需求,如果外層有事務,則當前事務加入到外層事務,一塊提交,一塊回滾。如果外層沒有事務,新建一個事務執(zhí)行
(2)PROPAGATION_REQUES_NEW
????????該事務傳播機制是每次都會新開啟一個事務,同時把外層事務掛起,當當前事務執(zhí)行完畢,恢復上層事務的執(zhí)行。如果外層沒有事務,執(zhí)行當前新開啟的事務即可
(3)PROPAGATION_SUPPORT
????????如果外層有事務,則加入外層事務,如果外層沒有事務,則直接使用非事務方式執(zhí)行。完全依賴外層的事務
(4)PROPAGATION_NOT_SUPPORT
????????該傳播機制不支持事務,如果外層存在事務則掛起,執(zhí)行完當前代碼,則恢復外層事務,無論是否異常都不會回滾當前的代碼
(5)PROPAGATION_NEVER
????????該傳播機制不支持外層事務,即如果外層有事務就拋出異常
(6)PROPAGATION_MANDATORY
????????與NEVER相反,如果外層沒有事務,則拋出異常
(7)PROPAGATION_NESTED
????????該傳播機制的特點是可以保存狀態(tài)保存點,當前事務回滾到某一個點,從而避免所有的嵌套事務都回滾,即各自回滾各自的,如果子事務沒有把異常吃掉,基本還是會引起全部回滾的。
四、事務的隔離級別
1、首先說明一下事務并發(fā)引起的三種情況:
(1) Dirty Reads 臟讀?
????????一個事務正在對數(shù)據(jù)進行更新操作,但是更新還未提交,另一個事務這時也來操作這組數(shù)據(jù),并且讀取了前一個事務還未提交的數(shù)據(jù),而前一個事務如果操作失敗進行了回滾,后一個事務讀取的就是錯誤數(shù)據(jù),這樣就造成了臟讀。
(2) Non-Repeatable Reads 不可重復讀?
????????一個事務多次讀取同一數(shù)據(jù),在該事務還未結束時,另一個事務也對該數(shù)據(jù)進行了操作,而且在第一個事務兩次次讀取之間,第二個事務對數(shù)據(jù)進行了更新,那么第一個事務前后兩次讀取到的數(shù)據(jù)是不同的,這樣就造成了不可重復讀。
(3) Phantom Reads 幻像讀?
????????第一個數(shù)據(jù)正在查詢符合某一條件的數(shù)據(jù),這時,另一個事務又插入了一條符合條件的數(shù)據(jù),第一個事務在第二次查詢符合同一條件的數(shù)據(jù)時,發(fā)現(xiàn)多了一條前一次查詢時沒有的數(shù)據(jù),仿佛幻覺一樣,這就是幻像讀。
補充注意--非重復度和幻像讀的區(qū)別:
????????非重復讀是指同一查詢在同一事務中多次進行,由于其他提交事務所做的修改或刪除,每次返回不同的結果集,此時發(fā)生非重復讀。
????????幻像讀是指同一查詢在同一事務中多次進行,由于其他提交事務所做的插入操作,每次返回不同的結果集,此時發(fā)生幻像讀。
????????表面上看,區(qū)別就在于非重復讀能看見其他事務提交的修改和刪除,而幻像能看見其他事務提交的插入。?
2、隔離級別
(1)DEFAULT (默認)?
????????這是一個PlatfromTransactionManager默認的隔離級別,使用數(shù)據(jù)庫默認的事務隔離級別。另外四個與JDBC的隔離級別相對應。
(2)Read UnCommitted(讀未提交)
? ? ? ? 這是事務最低級的隔離級別了,允許另外一個事務讀到這個事務未提交的數(shù)據(jù),會導致臟讀、不可重復讀和幻像讀的問題。
(3)Read Committed(讀已提交)
? ? ? ? 該級別是指只允許另外一個事務可以讀到這個事務已提交的數(shù)據(jù),避免了臟讀問題,但是不可避免不可重復讀和幻像讀的問題。
(4)REPEATABLE_READ (可重復讀)?
? ??????確保事務可以多次從一個字段中讀取相同的值,在此事務持續(xù)期間,禁止其他事務對此字段的更新,可以避免臟讀和不可重復讀,仍會出現(xiàn)幻讀問題。
(5)SERIALIZABLE(串行化)
????????確保事務可以從一個表中讀取相同的行,在這個事務持續(xù)期間,禁止其他事務對該表執(zhí)行插入、更新和刪除操作,可避免所有并發(fā)問題,但性能非常低。??
總結如下圖:

? ??????