Spring面試——事務
-
-
- 前言
- Spring事務的原理
- Spring事務復原
- Spring事務什麼情況下失效
- Spring的事務隔離和資料庫隔離是不是一個概念
- Sping事務中一個帶有事務的方法調用另一個帶事務的方法,預設開啟幾個事務
- 怎麼保證Spring事務内的連接配接唯一性
- 結束語
-
前言
之前面試被問過Spring事務的相關問題,結果因為總結的少,回答的支支吾吾,是以導緻被發儲備到人家公司的人才倉庫了。┭┮﹏┭┮,
是以接下來我就好好總結一下!
Spring事務的原理
Spring事務的本質就是資料庫對事物的支援。沒有資料庫對事物的支援,Spring是無法提供事務功能的。
那我平常用的最多的mysql來說,一般我們都是用JDBC來操作事務的,步驟如下:
- 擷取連接配接 Connection con = DriverManager.getConnection();
- 開啟事務 con.setAutoCommit(true(自動送出)/false(不自動送出));
- 執行我們的業務(CRUD) ;
- 處理成功,送出事務:con.commit() ;處理失敗,復原事務:con.rollback();
- 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中取得。是以能保證唯一,以為每一個線程的資料庫連接配接都是隔離的。
我們可以在下面的源碼看出來:
結束語
好了,總結完畢,共勉!