Spring Boot實戰之事務管理

什么是事務?

我們在開發企業應用時,對于業務人員的一個操作實際是對數據讀寫的多步操作的結合。由于數據操作在順序執行的過程中,任何一步操作都有可能發生異常,異常會導致后續操作無法完成,此時由于業務邏輯并未正確的完成,之前成功操作數據的并不可靠,需要在這種情況下進行回退。

事務的作用就是為了保證用戶的每一個操作都是可靠的,事務中的每一步操作都必須成功執行,只要有發生異常就回退到事務開始未進行操作的狀態。

事務管理是Spring框架中最為常用的功能之一,我們在使用Spring Boot開發應用時,大部分情況下也都需要使用事務。

快速入門

在Spring Boot中,當我們使用了spring-boot-starter-jdbc或spring-boot-starter-data-jpa依賴的時候(筆者持久層框架用的是mybatis,所以這個地方引入的依賴是spring-boot-starter-jdbc),框架會自動默認分別注入DataSourceTransactionManager或JpaTransactionManager。所以我們不需要任何額外配置就可以用@Transactional注解進行事務的使用。

下面我們來聊聊事物管理的幾個用途。

用途一:單元測試數據回滾

通常我們單元測試為了保證每個測試之間的數據獨立,可以使用@Transactional
注解讓每個單元測試都能在結束時回滾。而真正在開發業務邏輯時,我們通常在service層接口中使用@Transactional來對各個業務邏輯進行事務管理的配置。

下面是一個單元測試的例子:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
public class ApplicationTests {

@Autowired
private UserRepository userRepository;

@Test
public void test() throws Exception {

// 創建10條記錄
userRepository.save(new User("AAA", 10));
userRepository.save(new User("BBB", 20));
userRepository.save(new User("CCC", 30));
userRepository.save(new User("DDD", 40));
userRepository.save(new User("EEE", 50));
userRepository.save(new User("FFF", 60));
userRepository.save(new User("GGG", 70));
userRepository.save(new User("HHH", 80));
userRepository.save(new User("III", 90));
userRepository.save(new User("JJJ", 100));

// 省略后續的一些驗證操作(Assert等)
}

}

此時查數據庫中,創建了name從AAA到JJJ的記錄。而在通常的單元測試中,測試完成后需要能夠回退到測試之前的狀態,這時候就可以使用事務讓它實現回退,做法非常簡單,我們只需要在test函數上添@Transactional
注解即可。

@Test
@Transactional
public void test() throws Exception {

// 省略測試內容

}

再來執行該測試用例,可以看到控制臺中輸出了回滾日志(Rolled back transaction for test context),

2016-05-27 10:35:32.210 WARN 5672 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1406, SQLState: 22001
2016-05-27 10:35:32.210 ERROR 5672 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : Data truncation: Data too long for column 'name' at row 1
2016-05-27 10:35:32.213 WARN 5672 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Warning Code: 1406, SQLState: HY000
2016-05-27 10:35:32.213 WARN 5672 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : Data too long for column 'name' at row 1
2016-05-27 10:35:32.221 INFO 5672 --- [ main] o.s.t.c.transaction.TransactionContext : Rolled back transaction for test context [DefaultTestContext@1d7a715 testClass = ApplicationTests, testInstance = com.didispace.ApplicationTests@95a785, testMethod = test@ApplicationTests, testException = org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.DataException: could not execute statement, mergedContextConfiguration = [MergedContextConfiguration@11f39f9 testClass = ApplicationTests, locations = '{}', classes = '{class com.didispace.Application}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextLoader = 'org.springframework.boot.test.SpringApplicationContextLoader', parent = [null]]].

org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.DataException: could not execute statement

查看數據庫,發現此次單元測試執行完成之后,并沒有新增記錄,成功實現了自動回滾。

用途二:業務邏輯事務管理

真正在開發業務邏輯時,我們通常在service層接口中使用@Transactional
來對各個業務邏輯進行事務管理的配置,例如:

public interface UserService {

@Transactional
User login(String name, String password);

}

筆者注
此處需要特別注意,盡可能不要在Service層catch異常,因為@Transactional事務管理的默認觸發異常類型是RuntimeException,一旦在Service中catch了Exception,并且Exception的類型不是RumtimeException,那么將不會觸發回滾,這個是導致回滾失效的一個很容易犯的錯誤。具體可以參考文章 Spring事務異?;貪L,捕獲異常不拋出就不會回滾

事務詳解

上面的例子中我們使用了默認的事務配置,可以滿足一些基本的事務需求,但是當我們項目較大較復雜時(比如,有多個數據源等),這時候需要在聲明事務時,指定不同的事務管理器。對于不同數據源的事務管理配置可以見《Spring Boot多數據源配置與使用》中的設置。在聲明事務時,只需要通過value屬性指定配置的事務管理器名即可,例如:

@Transactional(value="transactionManagerPrimary")

除了指定不同的事務管理器之后,還能對事務進行隔離級別和傳播行為的控制,下面分別詳細解釋:

隔離級別

隔離級別是指若干個并發的事務之間的隔離程度,與我們開發時候主要相關的場景包括:臟讀取、重復讀、幻讀。
我們可以看org.springframework.transaction.annotation.Isolation
枚舉類中定義了五個表示隔離級別的值:

public enum Isolation {
DEFAULT(-1),
READ_UNCOMMITTED(1),
READ_COMMITTED(2),
REPEATABLE_READ(4),
SERIALIZABLE(8);
}
  • DEFAULT:這是默認值,表示使用底層數據庫的默認隔離級別。對大部分數據庫而言,通常這值就是:READ_COMMITTED
  • READ_UNCOMMITTED:該隔離級別表示一個事務可以讀取另一個事務修改但還沒有提交的數據。該級別不能防止臟讀和不可重復讀,因此很少使用該隔離級別。
  • READ_COMMITTED:該隔離級別表示一個事務只能讀取另一個事務已經提交的數據。該級別可以防止臟讀,這也是大多數情況下的推薦值。
  • REPEATABLE_READ:該隔離級別表示一個事務在整個過程中可以多次重復執行某個查詢,并且每次返回的記錄都相同。即使在多次查詢之間有新增的數據滿足該查詢,這些新增的記錄也會被忽略。該級別可以防止臟讀和不可重復讀。
  • SERIALIZABLE:所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾,也就是說,該級別可以防止臟讀、不可重復讀以及幻讀。但是這將嚴重影響程序的性能。通常情況下也不會用到該級別。

指定方法:通過使用isolation屬性設置,例如:

@Transactional(isolation = Isolation.DEFAULT)
傳播行為

所謂事務的傳播行為是指,如果在開始當前事務之前,一個事務上下文已經存在,此時有若干選項可以指定一個事務性方法的執行行為。
我們可以看org.springframework.transaction.annotation.Propagation
枚舉類中定義了7個表示傳播行為的枚舉值:

public enum Propagation {
REQUIRED(0),
SUPPORTS(1),
MANDATORY(2),
REQUIRES_NEW(3),
NOT_SUPPORTED(4),
NEVER(5),
NESTED(6);
}
  • REQUIRED:如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務。
  • SUPPORTS:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。
  • MANDATORY:如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。
  • REQUIRES_NEW:創建一個新的事務,如果當前存在事務,則把當前事務掛起。
  • NOT_SUPPORTED:以非事務方式運行,如果當前存在事務,則把當前事務掛起。
  • NEVER:以非事務方式運行,如果當前存在事務,則拋出異常。
  • NESTED:如果當前存在事務,則創建一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價于REQUIRED。

指定方法:通過使用propagation屬性設置,例如:

@Transactional(propagation = Propagation.REQUIRED)

通常來說一個完整的@Transactional注解如下:

@Transactional(value = "yourPlatformTransactionManager",propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = Exception.class)

參考文章:http://blog.didispace.com/springboottransactional/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內容

  • 什么是事務? 我們在開發企業應用時,對于業務人員的一個操作實際是對數據讀寫的多步操作的結合。由于數據操作在順序執行...
    程序猿DD閱讀 2,525評論 0 21
  • 18.7.2 概述 Spring Framework對事務管理提供了一致的抽象,其特點如下:為不同的事務API提供...
    靜心安分讀書閱讀 334評論 1 1
  • 1.數據庫事務基礎知識 1.1.何為數據庫事務 數據庫事務的4個特性 原子性:組成一個事務的多個數據庫操作是一個不...
    小螺釘12138閱讀 1,589評論 1 18
  • spring事務管理是指在業務代碼在出現異常之后,對之前的操作進行回滾,保證數據庫數據的一致性 分為編程式事務管理...
    MicoCube閱讀 8,910評論 0 3
  • 什么叫城市經濟? 我認為首先要明白什么叫經濟 才明白什么叫城市經濟 我從祖先那里了解到經濟是這么回事 經濟即為 經...
    木風恒閱讀 300評論 0 0