寶貝,來,講講spring事務有哪些坑?

引言

今天,我們接上文《面試官:談談你對mysql事務的認識》的內容,來講spring中和事務有關的考題!
因爲事務這塊,面試的出現機率很高。而大家工作中CRUD的比較多,沒有好好總結過這塊的知識,因此面試容易支支吾吾答不出來,於是乎接下來你就會接到一張好人卡,如"你很優秀,不適合我們公司!"

由於《面試官:談談你對mysql事務的認識》篇幅所限,因此略過了spring事務相關常見面試題,今天給大家補上!主要題目如下:

  • (1)spring事務的原理?

  • (2)spring什麼情況下進行事務回滾?

  • (3)spring事務什麼時候失效?

  • (4)Spring的事務和數據庫的事務隔離是一個概念麼?

  • (5)spring事務控制放在service層,在service方法中一個方法調用service中的另一個方法,默認開啓幾個事務?

  • (6)怎麼保證spring事務內的連接唯一性?

正文

1、spring事務的原理?
首先,我們先明白spring事務的本質其實就是數據庫對事務的支持,沒有數據庫的事務支持,spring是無法提供事務功能的。
那麼,我們一般使用JDBC操作事務的時候,代碼如下

  • (1)獲取連接 Connection con = DriverManager.getConnection()

  • (2)開啓事務con.setAutoCommit(true/false);

  • (3)執行CRUD

  • (4)提交事務/回滾事務 con.commit() / con.rollback();

  • (5)關閉連接 conn.close();

使用spring事務管理後,我們可以省略步驟(2)和步驟(4),就是讓AOP幫你去做這些工作。關鍵類在TransactionAspectSupport這個切面裏,大家有興趣自己去翻。我就不列舉了,因爲公衆號類型的文章,實在不適合寫一些源碼解析!

2、spring 什麼情況下進行事務回滾?
首先,我們要明白Spring事務回滾機制是這樣的:當所攔截的方法有指定異常拋出,事務纔會自動進行回滾!
因此,如果你默默的吞掉異常,像下面這樣

@Service
public class UserService{
    @Transactional
    public void updateUser(User user) {
        try {
            System.out.println("孤獨煙真帥");
            //do something
        } catch {
          //do something
        }
    }

}

那切面捕捉不到異常,肯定是不會回滾的。
還有就是,默認配置下,事務只會對Error與RuntimeException及其子類這些異常,做出回滾。一般的Exception這些Checked異常不會發生回滾(如果一般Exception想回滾要做出配置),如下所示

@Transactional(rollbackFor = Exception.class)

但是在實際開發中,我們會遇到這麼一種情況!就是並沒有異常發生,但是由於事務結果未滿足具體業務需求,所以我們需要手動回滾事務,於是乎方法也很簡單

  • (1)自己在代碼裏拋出一個自定義異常(常用)

  • (2)通過編程代碼回滾(不常用)

TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();

3、spring事務什麼時候失效?
ps:經典老題啊!!4年前我畢業那會在問,我都工作4年了,現在還問這道!其出現頻率,不下於HashMap的出現頻率!該問題有很多問法,例如spring事務有哪些坑?你用spring事務的時候,有遇到過什麼問題麼?其實答案都一樣的,OK,不羅嗦了,開始答案!

我們知道spring事務的原理是AOP,進行了切面增強,那麼失效的根本原因是這個AOP不起作用了!常見情況有如下幾種
(1)發生自調用
例如代碼如下

@Service
public class UserService{
   public void update(User user) {
        updateUser(user);
    }

    @Transactional
    public void updateUser(User user) {
        System.out.println("孤獨煙真帥");
        //do something
    }

}

此時是無效的,因此上面的代碼等同於

@Service
public class UserService{
   public void update(User user) {
        this.updateUser(user);
    }

    @Transactional
    public void updateUser(User user) {
        System.out.println("孤獨煙真帥");
        //do something
    }

}

此時這個this對象不是代理類,而是UserService對象本身!
解決方法很簡單,讓那個this變成UserService的代理類即可,就不展開說明了!

(2)方法不是public的
OK,我這裏不想舉源碼。大家想一個邏輯就行!
@Transactional註解的方法都是被外部其他類調用纔有效!
如果方法修飾符是private的,這個方法能被外部其他類調到麼?
既然調不到,事務生效有意義麼?
想通這套邏輯就行了~~

記住:@Transactional 註解只能應用到 public 可見度的方法上。如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 註解,它也不會報錯, 但是這個被註解的方法將不會有事務行爲。

ps:先這麼理解就好了,因爲真的去翻原因,就要貼代碼了,這文章可讀性就很差了。

(3)發生了錯誤異常
這個問題在第二問講過了,因爲默認回滾的是:RuntimeException。如果是其他異常想要回滾,需要在@Transactional註解上加rollbackFor屬性。
又或者是異常被吞了,事務也會失效,不贅述!

(4)數據庫不支持事務
畢竟spring事務用的是數據庫的事務,如果數據庫不支持事務,那spring事務肯定是無法生效滴!

OK,答到這裏就夠了!

可能有的讀者會說了

煙哥啊,其他文章裏說什麼數據源沒有配置事務管理器也會導致事務失效,你怎麼沒提?

OK,我爲什麼不提,因爲這種情況屬於你配置的不對!隨便少一個配置都會導致事務不生效,例如我們在Springboot中的Application類上不加註解@EnableTransactionManagement,也會使事務不生效,難道您能將每種情況下的配置背下來?這種配置的東西,臨時查詢即可!再比如,你把隔離級別配置成

@Transactional(propagation = Propagation.NOT_SUPPORTED)

該隔離級別表示不以事務運行,當前若存在事務則掛起,事務肯定不生效啊!這種屬於自己配錯的情況,如果真要舉例,面試官也不愛聽的!在面試中,一句"配置錯誤也會導致事務不生效,例如xxx配置,舉一兩個即可!"

4、Spring的事務隔離和數據庫的事務隔離是一個概念麼?
OK,是一回事!
我們先明確一點,數據庫一般有四種隔離級別
數據庫有四種隔離級別分別爲

  • read uncommitted(未提交讀)

  • read committed(提交讀、不可重複讀)

  • repeatable read(可重複讀)

  • serializable(可串行化)

而spring只是在此基礎上抽象出一種隔離級別爲default,表示以數據庫默認配置的爲主。例如,mysql默認的事務隔離級別爲repeatable-read。而Oracle 默認隔離級別爲讀已提交。

於是乎,有一個經典問題是這麼問的

我數據庫的配置隔離級別是Read Commited,而Spring配置的隔離級別是Repeatable Read,請問這時隔離級別是以哪一個爲準?

OK,以Spring配置的爲準。JDBC有一個接口是這樣的

void setTransactionIsolation(int level) throws SQLException;

該接口用來設置事務的隔離級別。
那麼在DataSourceUtils中,有一段代碼是這樣的


他的意思就是,如果spring定義的隔離級別和數據庫的不一樣,則以spring定義的爲準。
另外,如果spring設置的隔離級別數據庫不支持,效果取決於數據庫。

5、spring事務控制放在service層,在service方法中一個方法調用service中的另一個方法,默認開啓幾個事務?
此題考查的是spring的事務傳播行爲
我們都知道,默認的傳播行爲是PROPAGATION_REQUIRED,如果外層有事務,則當前事務加入到外層事務,一塊提交,一塊回滾。如果外層沒有事務,新建一個事務執行!
也就是說,默認情況下只有一個事務!

當然這種時候如果面試官繼續追問其他傳播行爲的情形,如何回答?

那我們應該?我們應該?把每種傳播機制都拿出來講一遍?沒必要,這種時候直接掀桌子走人。因爲你就算背下來了,過幾天還是忘記。用到的時候,再去查詢即可。

6、怎麼保證spring事務內的連接唯一性?
這道題很多種問法,例如Spring 是如何保證事務獲取同一個Connection的?

OK,開始我們的講解!其實答案只有一句話,因爲那個Connection在事務開始時封裝在了ThreadLocal裏,後面事務執行過程中,都是從ThreadLocal中取的,肯定能保證唯一,因爲都是在一個線程中執行的!

至於代碼。。。以JDBCTemplate的execute方法爲例,看看下面那張圖就懂了。

總結

探討了spring事務中常見面試題,希望大家有所收穫!

本系列其他文章如下:

《面試官:講講mysql表設計要注意啥》
《面試官:談談對mysql索引的認識》
《面試官:談談你對mysql聯合索引的認識?》

《面試官:談談你對mysql事務的認識?》


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