異常與事務的三個小坑(開發中常見)

2021-09-02 05:37:36 字數 2200 閱讀 9983

在實際專案中,使用事務是很簡單的,例如在spring boot專案(或者說是spring專案)中,乙個@transactional 註解就可以解決。但是事務有很多小坑在等著我們。

在開發過程中,業務層已經考慮到了異常,或者編輯器已經提示我們丟擲異常了,但是,並不是我們把異常丟擲來了,有異常了事務就會回滾。例子。

@service

public class userserviceimpl implements userservice

}

手動丟擲乙個 sqlexception 來模擬實際中運算元據庫發生的異常,在這個方法中,既然丟擲了異常,那麼事務應該回滾,實際卻不如此,讀者可以自己測試一下就會發現,仍然是可以往資料庫插入一條使用者資料的。

那麼問題出在哪呢?因為spring boot 預設的事務規則是遇到執行異常(runtimeexception)和程式錯誤(error)才會回滾。比如上面我們的例子中如果丟擲的 runtimeexception 就沒有問題,但是丟擲 sqlexception 就無法回滾了。

針對非執行時異常如果要進行事務回滾的話,可以在 @transactional 註解中使用 rollbackfor 屬性來指定異常,比如:

@transactional(rollbackfor = exception.class)
這樣就沒有問題了,所以在實際專案中,一定要指定異常,這是大部分開發人員不注意的地方。

在現實專案中,我們在處理異常時,有兩種方式,要麼丟擲去,讓上一層來捕獲處理;要麼把異常 try...catch 掉,在異常出現的地方給處理掉。就因為有這個 try...catch,所以導致異常被 「吃」 掉,事務無法回滾。我們還是看上面那個例子,只不過簡單修改一下**:

@service

public class userserviceimpl implements userservice catch (exception e)

}}

測試發現,仍是可以插入一條資料的,說明事務並沒有因為異常丟擲而回滾。這就是 try...catch 把異 「吃」 掉了,這個細節往往比上面那個坑更難以發現,因為我們的思維方式很容易導致 try...catch **的產生,一旦出現這種問題,往往排查起來比較費勁。這個就是很明顯的自己給自己挖坑,而且自己掉進去之後,還出不來。

那這種怎麼解決呢?直接往上拋,給上一層來處理即可,千萬不要在事務中把異常自己 」吃「 掉

事務範圍這個東西比上面兩個坑埋的更深!我之所以把這個也寫上,是因為這是我之前在實際專案中遇到的,該場景我就不模擬了,我寫乙個 demo 讓大家看一下,把這個坑記住即可,以後在寫**時,遇到併發問題,如果能想到這個坑,那麼這篇文章也就有價值了。

@service

public class userserviceimpl implements userservice

}

可以看到,因為要考慮併發問題,在業務層**的方法上加了個 synchronized 關鍵字。我舉個實際的場景,比如乙個資料庫中,針對某個使用者,只有一條記錄,下乙個插入動作過來,會先判斷該資料庫中有沒有相同的使用者,如果有就不插入,就更新,沒有才插入,所以理論上,資料庫中永遠就一條同一使用者資訊,不會出現同一資料庫中插入了兩條相同使用者的資訊。 

但是在壓測時,就會出現上面的問題,資料庫中確實有兩條同一使用者的資訊,那說明 synchronized 並沒有起到作用。分析其原因,在於事務的範圍和鎖的範圍問題。

從上面方法中可以看到,方法上是加了事務的,那麼也就是說,在執行該方法開始時,事務啟動,執行完了後,事務關閉。但是 synchronized 沒有起作用,其實根本原因是因為事務的範圍比鎖的範圍大。也就是說,在加鎖的那部分**執行完之後,鎖釋放掉了,但是事務還沒結束,就在此時另乙個執行緒進來了,事務沒結束的話,第二個執行緒進來時,資料庫的狀態和第乙個執行緒剛進來是一樣的。即由於mysql innodb引擎的預設隔離級別是可重複讀(在同乙個事務裡,select的結果是事務開始時時間點的狀態),執行緒二事務開始的時候,執行緒一還沒提交完成,導致讀取的資料還沒更新。第二個執行緒也做了插入動作,導致了髒資料。

這個問題可以避免,第一,把事務去掉即可(不推薦);第二,在呼叫該 service 的地方加鎖,保證鎖的範圍比事務的範圍大即可。

這三個小坑在實際開發中經常遇到,希望能給讀者一些啟發,如果你覺得有用,請**給更多的人。

異常的三個部分

異常分三個部分 1,產生異常 new 乙個異常類的物件 2,丟擲異常 throw new parseexception 這 拋在本地 3,解決異常物件 trycatch finallye dt z 0 t z 1e tdt 你可以找到更多關於的資訊latex數學表示式here.可以使用uml圖表進行...

Spring Boot中常用的三個註解

這個註解就是根據 configuration 註解演化而來的,二者功能也一致,標註當前類是配置類。備註 以上兩個註解會將當前類內宣告的乙個或多個以 bean 註解標記的方法的例項納入到spring容器中,並且例項名就是方法名,見下圖。這是 spring 3.1 新增的乙個註解,用來代替配置檔案中的 ...

C 異常處理程式設計的三個境界

這是上一次看完herb sutter的 exceptional c 後形成的看法,因為懶於更新blog,一直沒有寫下來。一般講到三個境界,很多人會聯想到 1見山是山,見水是水 2見山不是山,見水不是水 3見山還是山,見水還是水。嗯沒錯,區區這裡說的也是這東西,只不過是有關程式設計,有關c 有關異常而...