天天看點

Spring面試——事務

Spring面試——事務

      • 前言
      • Spring事務的原理
      • Spring事務復原
      • Spring事務什麼情況下失效
      • Spring的事務隔離和資料庫隔離是不是一個概念
      • Sping事務中一個帶有事務的方法調用另一個帶事務的方法,預設開啟幾個事務
      • 怎麼保證Spring事務内的連接配接唯一性
      • 結束語

前言

之前面試被問過Spring事務的相關問題,結果因為總結的少,回答的支支吾吾,是以導緻被發儲備到人家公司的人才倉庫了。┭┮﹏┭┮,

是以接下來我就好好總結一下!

Spring事務的原理

Spring事務的本質就是資料庫對事物的支援。沒有資料庫對事物的支援,Spring是無法提供事務功能的。

那我平常用的最多的mysql來說,一般我們都是用JDBC來操作事務的,步驟如下:

  1. 擷取連接配接 Connection con = DriverManager.getConnection();
  2. 開啟事務 con.setAutoCommit(true(自動送出)/false(不自動送出));
  3. 執行我們的業務(CRUD) ;
  4. 處理成功,送出事務:con.commit() ;處理失敗,復原事務:con.rollback();
  5. finally關閉連接配接 conn.close()。

如果沒有Spring對事務的支援,那麼我們每次執行事務都需要手動處理以上的五個步驟,但是其實對于步驟2和步驟4,我們是不是可以把他從業務代碼裡面分離出來呢?這時候我們想到了熟悉的AOP,Spring事務就是基于AOP幫我們處理了這兩個步驟。

Spring事務復原

什麼時候會發生事務復原呢?當所代理的方法有指定的異常抛出,事務才會自動進行復原。

是以我們在加了事務的方法裡面,千萬不要默默地吞掉異常!如下的做法是錯誤的。

@Service
@Slfj4
public class UserService{
    @Transactional
    public void addUser(User user) {
        try {
            log.inf("新增一個使用者");
            //do something
        } catch {
          //do something
        }
    }
}
           

一般情況我們都是自定義一個業務異常抛出,最好繼承RuntimeException類。

為什麼要最好繼承自RuntimeException?因為在預設的配置下,事務隻會對Error與RuntimeException及其子類這些異常做復原。一半的Exception這些Checked異常不會復原,如果想要一般的Exception也會滾,可以做以下的配置:

我們順帶說一下java異常的分類吧,這個我也被問過多次!

拿幾個比較常見的異常類來說吧:

  • OutOfMemoryError:這個屬于Error錯誤,是系統無法控制的,Error一般指的是系統方面的異常,比如 藍屏,記憶體溢出,jvm運作環境出現了問題,,它隸屬于unchecked異常,因為無法被預知。。
  • IOException:這個屬于check異常,除了RuntimeException及其子類,Exception所有的子類都屬于check異常,check異常相當于編譯器提前預知到程式中将會出現的異常,一般這種異常需要我們強制手動try,catch或者抛出。
  • NullPointerException:這個屬于unchecked異常,因為他繼承自RuntimeException,這類異常一般是無可預知的,是程式裡面不應該出現的異常,很多都是因為程式bug而抛出的異常。

他們都繼承于同一個基類:Throwable,用來定義所有可以作為異常被抛出來的類。

Spring事務什麼情況下失效

我使用的過程中好像沒遇到過這種情況,但是被問得次數還是蠻多的。

Spring的事務的原理是AOP,對代理的類進行切面增強,是以失效的原因就是這個代理不起作用了,一般常見的情況如下。

  • 發生自調用:說得簡單點就是類裡面的方法調用它同一個類的方法,如下:
    @Service
    public class OrderServiceImpl implements OrderService {
        @Transactional
        public void update(Order order) {
            updateOrder(order); 
       }
        @Transactional(propagation = Propagation.REQUIRES_NEW)
        public void updateOrder(Order order) {
            // update order;
        }
    }
               
    那麼這種情況我們怎麼處理呢,如下,我麼可以取容器裡面拿這個類的代理類來處理。
    @Service
    	public class OrderServiceImpl implements OrderService {
    	    @Transactional
    	    public void update(Order order) {
    	    	((OrderServiceImpl ) AopContext.currentProxy()).updateOrder(order);
    	   }
    	    @Transactional(propagation = Propagation.REQUIRES_NEW)
    	    public void updateOrder(Order order) {
    	        // update order;
    	    }
    	}
               
  • 方法修飾符不是public:@Transactional 注解隻能應用到 public 方法上。如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不會報錯, 但是這個被注解的方法将不會以事務的方式運作。
  • 發生了錯誤的異常:上面已經提到過,Spring事務預設隻對RuntimeException異常以及其子類起作用,如果我們想要其他的check異常也能復原,那麼我們需要在@Transactional 注解上加rollbackFor = Exception.class。
  • 異常被程式吞了:異常被程式吞了,導緻@Transactional 失效,是以一般我們吞完異常之後可以抛出一個業務異常。
  • 資料庫不支援事務:Spring事務是基于資料庫事務的,如果資料庫都不支援事務,那麼Spring事務配置了也白費。
  • 資料源沒有配置事務管理器:emmm,這種錯誤一般隻有在項目剛開始的時候可能會發生吧(忘記配置@EnableTransactionManagement)
  • 資料庫隔離等級設定有誤:隔離等級設定成不支援事務。

Spring的事務隔離和資料庫隔離是不是一個概念

資料可以一般有以下四個隔離等級:

  • Read Uncommitted:未送出讀,會出現髒讀,不可重複讀,幻讀;
  • Read Committed:送出讀,會出現不可重複讀,幻讀;
  • Repeatable Read:可重複讀,會出現幻讀;
  • Serializable:可串行化。

MySQL預設的事務隔離級别是Repeatable Read,Oracle事務預設隔離級别是Read Committed。

spring的事務隔離等級配置優先級要高于資料庫的事務隔離等級設定,但是如果spring設定的事務隔離等級資料庫不支援,那麼以資料庫的為準。

Sping事務中一個帶有事務的方法調用另一個帶事務的方法,預設開啟幾個事務

這個問題,一般是考察我們對事務傳播行為的了解。

spring事務預設的傳播行為是PROPAGATION_REQUIRED。就是如果目前有事務,加入目前事務;如果沒有事務,建立一個事務執行。也就是預設情況下隻有一個事務。

事物的傳播性質一共有七種:

  • PROPAGATION_REQUIRED:支援目前事務,如果目前沒有事務,就建立一個事務。這是最常見的選擇。
  • PROPAGATION_SUPPORTS:支援目前事務,如果目前沒有事務,就以非事務方式執行。
  • PROPAGATION_MANDATORY:支援目前事務,如果目前沒有事務,就抛出異常。
  • PROPAGATION_REQUIRES_NEW:建立事務,如果目前存在事務,把目前事務挂起。
  • PROPAGATION_NOT_SUPPORTED:以非事務方式執行操作,如果目前存在事務,就把目前事務挂起。
  • PROPAGATION_NEVER:以非事務方式執行,如果目前存在事務,則抛出異常。
  • PROPAGATION_NESTED:如果沒有,就建立一個事務;如果有,就在目前事務中嵌套其他事務。

怎麼保證Spring事務内的連接配接唯一性

資料庫連接配接在事務開始的時候就存在了ThreadLocal裡面了,後面執行過程中,都是從ThreadLocal中取得。是以能保證唯一,以為每一個線程的資料庫連接配接都是隔離的。

我們可以在下面的源碼看出來:

Spring面試——事務
Spring面試——事務
Spring面試——事務
Spring面試——事務

結束語

好了,總結完畢,共勉!