面試之spring5種數據隔離和7種事務傳播性

如果不考慮隔離性引發的安全性問題:

髒讀:一個事務讀到了另一個事務未提交的數據
不可重複讀:一個事務督導另一個事務已經提交的update的數據導致多次查詢結果不一致
虛幻讀:一個事務讀到了另一個事務已經提交的insert的數據導致多次查詢結果不一致。

解決讀問題:設置事務隔離級別(5種)

DEFAULT這是一個PlatfromTransactionManager默認的隔離級別,使用數據庫默認的事務隔離級別;
未提交讀(read uncommited):髒讀,不可重複讀,虛讀都有可能發生
已提交讀(read commited):避免髒讀。但是不可重複讀和虛讀都有可能發生;
可重複讀(repeatable read):避免髒讀和不可重複讀,但是虛讀有可能發生;
串行化的(serializable):避免以上所有讀問題。
MySQL默認:可重複讀
Oracle默認:已提交讀
 

1.1 什麼是事務的傳播性

事務的傳播性一般在事務嵌套時候使用,比如在事務A裏面調用了另外一個使用事務的方法,那麼這倆個事務是各自作爲獨立的事務執行提交,還是內層的事務合併到外層的事務一塊提交那,這就是事務傳播性要確定的問題。下面一一介紹比較常用的事務傳播性。

事務讀傳播行爲

PROPAGION_XXX:事務的傳播行爲
*保證同一個事務中
PROPAGATION_REQUIRED支持當前事務,如果不存在,就新建一個(默認)
PROPAGATION_SUPPORTS支持當前事務,如果不存在,就不適用事務
PROPAGATION_MANDATORY 支持當前事務,如果不存在,拋出異常
*保證沒有在同一個事務中
PROPAGATION_REQUIRES_NEW如果有事務存在,掛起當前事務,創建一個新的事務
PROPAGATION_NOT_SUPPORTED 以非事務方式運行,如果有事務存在,掛起當前事務
PROPAGATION_NEVER 以非事務方式運行,如果有事務存在,拋出異常
PROPAGATION_NESTED 如果當前事務存在,則嵌套事務執行

1.2 PROPAGATION_REQUIRED

Spring默認的事務傳播機制,如果外層有事務則當前事務加入到外層事務,一塊提交一塊回滾,如果外層沒有事務則當前開啓一個新事務。這個機制可以滿足大多數業務場景。

試驗:

 public class BoA {
        public void test(){
        boB.sayHello();
    }
}

BoA 和boB都是進行過事務增強後的bo,那麼在執行test的時候會開啓一個事務(或者test調用方已經存在事務則加入該事務),執行到sayHello()時候由於傳播性是PROPAGATION_REQUIRED,所以sayHello方法加入到test的事務,那麼sayHello和test就會同時提交,同時回滾。值得注意的是如果test裏面調用sayHello時候加了trycatch沒有把異常跑出去,而sayHello方法卻拋出了異常,那麼整個事務也會回滾,這時候調用test的外層會受到"Transaction rolled back because it has been marked as rollback-only的異常,而把sayHello真正的異常喫掉了。

平時我們都是在bo裏面調用數據庫操作,在rpc和screen調用bo,所以bo層不應該catch掉異常,而應該拋出來,在rpc和screen層catch異常。

1.3 PROPAGATION_REQUIRES_NEW

該傳播機制是每次新開啓一個事務,同時把外層的事務掛起,當前新事務執行完畢後在恢復上層事務的執行。

以上面代碼爲例,首先進入test方法前會開啓一個事務,然後調用sayHello時候會把test的事務掛起,從新開啓一個新事務執行sayHello,執行完畢後恢復test的事務。如果sayHello拋出來異常則sayHello的事務會回滾,那麼test方法是否回滾那?這個要看情況,如果test在調用sayHello 時候使用了trycatch並且異常沒有在catch中throw出來,那麼test方法不會回滾,這時候sayHello是提交和回滾對test沒有影響,。 如果test中沒有加trycatch那麼,test也會回滾。

1.4 PROPAGATION_SUPPORTS

該傳播機制如果外層有事務則加入該事務,如果不存在也不會創建新事務,直接使用非事務方式執行。

以上面代碼爲例,由於PROPAGATION_SUPPORTS,所以test和sayHello都沒有開啓事務,沒啥好講的。

下面看下如果test隔離級別是PROPAGATION_REQUIRED,sayHello傳播性是PROPAGATION_SUPPORTS的情況。這時候外層test會開啓一個事務(或者test調用方已經存在事務則加入該事務),然後sayHello執行時候會加入到test的事務和1.2類似,同時提交同時回滾。

1.5 PROPAGATION_NOT_SUPPORTED

該傳播機制不支持事務,如果外層存在事務則掛起外層事務 ,然後執行當前邏輯,執行完畢後,恢復外層事務。

同樣這裏看下如果test使用PROPAGATION_REQUIRED,sayHello傳播性是PROPAGATION_NOT_SUPPORTED的情況,首先test會開啓一個事務(或者test調用方已經存在事務則加入該事務),然後sayHello執行時候會掛起該事務然後在非事務內做自己的事情,做完後在恢復test的事務。 無論sayHello是否拋出異常,sayHello的事務都不會回滾,因爲它不在事務範圍內,那test? 這個就和1.3一樣了,如果test catch住sayHello的異常沒有throw出去,那麼test就不回滾,否者回滾。

1.6 PROPAGATION_NEVER

該傳播機制不支持事務,如果外層存在事務則直接拋出異常。 IllegalTransactionStateException( "Existing transaction found for transaction marked with propagation 'never'")

1.7 PROPAGATION_MANDATORY

該傳播機制是說配置了該傳播性的方法只能在已經存在事務的方法中被調用,如果在不存在事務的方法中被調用,會拋出異常。 IllegalTransactionStateException( "No existing transaction found for transaction marked with propagation 'mandatory'");

1.8 PROPAGATION_NESTED

該傳播機制特點是可以保存狀態保存點,當事務回滾後會回滾到某一個保存點上,從而避免所有嵌套事務都回滾。

上面代碼test和sayHello都設置爲PROPAGATION_NESTED,如果sayHello拋出異常,兩者還是都回滾了,因爲sayHello雖然回滾到了savePoint而savepoint裏面確實包含了test的操作,但是savepoint後還是會吧異常throw給test,這導致了test的回滾。

總結,只有傳播性爲PROPAGATION_REQUIRED||PROPAGATION_REQUIRES_NEW||PROPAGATION_NESTED時候纔可能開啓一個新事務。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章