一次與面試官:情景對話的一問一答,例數 Spring 事務的那些坑,你有遇到過嗎?

墨眉無鋒墨家程式碼發表於2020-11-11

一次與面試官:情景對話的一問一答,例數 Spring 事務的那些坑,你有遇到過嗎?
今天,我們來講 Spring 中和事務有關的考題。

因為事務這塊,面試的出現機率很高。而大家工作中 CRUD 的比較多,沒有好好總結過這塊的知識,因此面試容易支支吾吾答不出來。於是乎接下來你就會接到一張好人卡,如"你很優秀,不適合我們公司!"

主要內容如下:

Spring 事務的原理;
Spring 什麼情況下進行事務回滾;
Spring 事務什麼時候失效;
Spring 事務和資料庫事務隔離是不是同一個概念;
Spring 事務控制放在 Service 層,在 Service 方法中一個方法呼叫 Service 中的另一個方法,預設開啟幾個事務;
怎麼保證 Spring 事務內的連線唯一性。

最新2020整理收集的一線網際網路公司面試真題(都整理成文件),有很多幹貨,包含netty,spring,執行緒,spring cloud等詳細講解,也有詳細的學習規劃圖,面試題整理等,我感覺在面試這塊講的非常清楚:獲取面試資料只需:點選這裡領取!!!暗號:CSDN在這裡插入圖片描述

1. Spring 事務的原理

首先,我們先明白 Spring 事務的本質其實就是資料庫對事務的支援。沒有資料庫的事務支援,Spring 是無法提供事務功能的。

那麼,我們一般使用 JDBC 操作事務的程式碼如下:

獲取連線 Connection con = DriverManager.getConnection();
開啟事務 con.setAutoCommit(true/false);
執行 CRUD;
提交事務、回滾事務:con.commit() ,con.rollback();
關閉連線 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)

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

自己在程式碼裡丟擲一個自定義異常(常用);
通過程式設計用程式碼回滾(不常用)。

TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();

3. Spring 事務什麼時候失效

注意:這是一道經典題。4年前我畢業那會在問,我都工作4年了,現在還問這道。其出現頻率,不亞於 HashMap 的出現頻率!

該問題有很多問法,例如 Spring 事務有哪些坑?你用 Spring 事務的時候,有遇到過什麼問題麼?其實答案都一樣的。OK,不羅嗦了,開始答案!

我們知道 Spring 事務的原理是 AOP,進行了切面增強,那麼失效的根本原因是這個 AOP 不起作用了。

常見情況有以下幾種:

3.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 的代理類即可,就不展開說明了。

3.2 方法修飾符不是 public

OK,我這裡不想舉原始碼。大家想一個邏輯就行:

@Transactional 註解的方法都是被外部其他類呼叫才有效,那麼如果方法修飾符是 private 的,這個方法能被外部其他類調到麼?

既然調不到,事務生效有意義嗎?想通這套邏輯就行了。

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

先這麼理解就好了,因為真的去翻原因就要貼程式碼了,這文章可讀性就很差了。

3.3 發生了錯誤的異常

這個問題在第二問講過了,因為預設回滾的是:RuntimeException。如果是其他異常想要回滾,需要在 @Transactional 註解上加 rollbackFor 屬性。

又或者是異常被吞了,事務也會失效,這裡不再贅述。

3.4 資料庫不支援事務

畢竟 Spring 事務用的是資料庫的事務,如果資料庫不支援事務,那 Spring 事務肯定是無法生效滴。

OK,答到這裡就夠了。

可能有的讀者會說:

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

OK,我為什麼不提,因為這種情況屬於你配置的不對。隨便少一個配置都會導致事務不生效,例如我們在 Spring Boot 中的 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 Committed。

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

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

答案是以 Spring 配置的為準。JDBC 有一個介面是這樣的:在這裡插入圖片描述
意思就是,如果 Spring 定義的隔離級別和資料庫的不一樣,則以 Spring 定義的為準。
另外,如果 Spring 設定的隔離級別資料庫不支援,設定的效果取決於資料庫。

5. Spring 事務控制放在 Service 層,在 Service 方法中一個方法呼叫 Service 中的另一個方法,預設開啟幾個事務

此題考查的是 Spring 的事務傳播行為。

我們都知道,預設的傳播行為是 PROPAGATION_REQUIRED。如果外層有事務,則當前事務加入到外層事務,一起提交併一起回滾;如果外層沒有事務,新建一個事務執行。也就是說,預設情況下只有一個事務。

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

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

6. 怎麼保證 Spring 事務內的連線唯一性

這道題很多種問法,例如 Spring 是如何保證事務獲取的是同一個 Connection?

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

至於程式碼,以J DBCTemplate的execute 方法為例,看看下面那張圖就懂了。
在這裡插入圖片描述

文末彩蛋

最新2020整理收集的一線網際網路公司面試真題(都整理成文件),有很多幹貨,包含netty,spring,執行緒,spring cloud等詳細講解,也有詳細的學習規劃圖,面試題整理等,我感覺在面試這塊講的非常清楚:獲取面試資料只需:點選這裡領取!!!暗號:CSDN在這裡插入圖片描述
在這裡插入圖片描述

相關文章