Spring Boot中的事務(wù)管理

什么是事務(wù)闷供?

我們在開發(fā)企業(yè)應(yīng)用時碎赢,對于業(yè)務(wù)人員的一個操作實際是對數(shù)據(jù)讀寫的多步操作的結(jié)合。由于數(shù)據(jù)操作在順序執(zhí)行的過程中账蓉,任何一步操作都有可能發(fā)生異常,異常會導(dǎo)致后續(xù)操作無法完成逾一,此時由于業(yè)務(wù)邏輯并未正確的完成铸本,之前成功操作數(shù)據(jù)的并不可靠,需要在這種情況下進(jìn)行回退遵堵。

事務(wù)的作用就是為了保證用戶的每一個操作都是可靠的箱玷,事務(wù)中的每一步操作都必須成功執(zhí)行,只要有發(fā)生異常就回退到事務(wù)開始未進(jìn)行操作的狀態(tài)陌宿。

事務(wù)管理是Spring框架中最為常用的功能之一锡足,我們在使用Spring Boot開發(fā)應(yīng)用時,大部分情況下也都需要使用事務(wù)限番。

快速入門

在Spring Boot中舱污,當(dāng)我們使用了spring-boot-starter-jdbc或spring-boot-starter-data-jpa依賴的時候,框架會自動默認(rèn)分別注入DataSourceTransactionManager或JpaTransactionManager弥虐。所以我們不需要任何額外配置就可以用@Transactional注解進(jìn)行事務(wù)的使用扩灯。

我們以之前實現(xiàn)的《用spring-data-jpa訪問數(shù)據(jù)庫》的示例Chapter3-2-2作為基礎(chǔ)工程進(jìn)行事務(wù)的使用常識。

在該樣例工程中(若對該數(shù)據(jù)訪問方式不了解霜瘪,可先閱讀該文章)珠插,我們引入了spring-data-jpa,并創(chuàng)建了User實體以及對User的數(shù)據(jù)訪問對象UserRepository颖对,在ApplicationTest類中實現(xiàn)了使用UserRepository進(jìn)行數(shù)據(jù)讀寫的單元測試用例捻撑,如下:

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

    @Autowired
    private UserRepository userRepository;

    @Test
    public void test() throws Exception {

        // 創(chuàng)建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));

        // 省略后續(xù)的一些驗證操作
    }


}

可以看到,在這個單元測試用例中缤底,使用UserRepository對象連續(xù)創(chuàng)建了10個User實體到數(shù)據(jù)庫中顾患,下面我們?nèi)藶榈膩碇圃煲恍┊惓#纯磿l(fā)生什么情況个唧。

通過定義User的name屬性長度為5江解,這樣通過創(chuàng)建時User實體的name屬性超長就可以觸發(fā)異常產(chǎn)生。

@Entity
public class User {

    @Id
    @GeneratedValue
    private Long id;

    @Column(nullable = false, length = 5)
    private String name;

    @Column(nullable = false)
    private Integer age;

    // 省略構(gòu)造函數(shù)徙歼、getter和setter

}

修改測試用例中創(chuàng)建記錄的語句犁河,將一條記錄的name長度超過5鳖枕,如下:name為HHHHHHHHH的User對象將會拋出異常。


// 創(chuàng)建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("HHHHHHHHHH", 80));
userRepository.save(new User("III", 90));
userRepository.save(new User("JJJ", 100));

執(zhí)行測試用例桨螺,可以看到控制臺中拋出了如下異常宾符,name字段超長:

2016-05-27 10:30:35.948  WARN 2660 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 1406, SQLState: 22001
2016-05-27 10:30:35.948 ERROR 2660 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : Data truncation: Data too long for column 'name' at row 1
2016-05-27 10:30:35.951  WARN 2660 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Warning Code: 1406, SQLState: HY000
2016-05-27 10:30:35.951  WARN 2660 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : Data too long for column 'name' at row 1

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

此時查數(shù)據(jù)庫中,創(chuàng)建了name從AAA到GGG的記錄灭翔,沒有HHHHHHHHHH魏烫、III、JJJ的記錄缠局。而若這是一個希望保證完整性操作的情況下则奥,AAA到GGG的記錄希望能在發(fā)生異常的時候被回退考润,這時候就可以使用事務(wù)讓它實現(xiàn)回退狭园,做法非常簡單,我們只需要在test函數(shù)上添加@Transactional注解即可糊治。

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

    // 省略測試內(nèi)容

}

再來執(zhí)行該測試用例唱矛,可以看到控制臺中輸出了回滾日志(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

再看數(shù)據(jù)庫中井辜,User表就沒有AAA到GGG的用戶數(shù)據(jù)了绎谦,成功實現(xiàn)了自動回滾。

這里主要通過單元測試演示了如何使用@Transactional注解來聲明一個函數(shù)需要被事務(wù)管理粥脚,通常我們單元測試為了保證每個測試之間的數(shù)據(jù)獨立窃肠,會使用@Rollback注解讓每個單元測試都能在結(jié)束時回滾。而真正在開發(fā)業(yè)務(wù)邏輯時刷允,我們通常在service層接口中使用@Transactional來對各個業(yè)務(wù)邏輯進(jìn)行事務(wù)管理的配置冤留,例如:


public interface UserService {
    
    @Transactional
    User login(String name, String password);
    
}

事務(wù)詳解

上面的例子中我們使用了默認(rèn)的事務(wù)配置,可以滿足一些基本的事務(wù)需求树灶,但是當(dāng)我們項目較大較復(fù)雜時(比如纤怒,有多個數(shù)據(jù)源等),這時候需要在聲明事務(wù)時天通,指定不同的事務(wù)管理器泊窘。對于不同數(shù)據(jù)源的事務(wù)管理配置可以見《Spring Boot多數(shù)據(jù)源配置與使用》中的設(shè)置。在聲明事務(wù)時像寒,只需要通過value屬性指定配置的事務(wù)管理器名即可烘豹,例如:@Transactional(value="transactionManagerPrimary")

除了指定不同的事務(wù)管理器之后诺祸,還能對事務(wù)進(jìn)行隔離級別和傳播行為的控制携悯,下面分別詳細(xì)解釋:

隔離級別

隔離級別是指若干個并發(fā)的事務(wù)之間的隔離程度,與我們開發(fā)時候主要相關(guān)的場景包括:臟讀取序臂、重復(fù)讀蚌卤、幻讀实束。

我們可以看org.springframework.transaction.annotation.Isolation枚舉類中定義了五個表示隔離級別的值:

public enum Isolation {
    DEFAULT(-1),
    READ_UNCOMMITTED(1),
    READ_COMMITTED(2),
    REPEATABLE_READ(4),
    SERIALIZABLE(8);
}
  • DEFAULT:這是默認(rèn)值,表示使用底層數(shù)據(jù)庫的默認(rèn)隔離級別逊彭。對大部分?jǐn)?shù)據(jù)庫而言咸灿,通常這值就是:READ_COMMITTED
  • READ_UNCOMMITTED:該隔離級別表示一個事務(wù)可以讀取另一個事務(wù)修改但還沒有提交的數(shù)據(jù)侮叮。該級別不能防止臟讀和不可重復(fù)讀避矢,因此很少使用該隔離級別。
  • READ_COMMITTED:該隔離級別表示一個事務(wù)只能讀取另一個事務(wù)已經(jīng)提交的數(shù)據(jù)囊榜。該級別可以防止臟讀审胸,這也是大多數(shù)情況下的推薦值。
  • REPEATABLE_READ:該隔離級別表示一個事務(wù)在整個過程中可以多次重復(fù)執(zhí)行某個查詢卸勺,并且每次返回的記錄都相同砂沛。即使在多次查詢之間有新增的數(shù)據(jù)滿足該查詢,這些新增的記錄也會被忽略曙求。該級別可以防止臟讀和不可重復(fù)讀碍庵。
  • SERIALIZABLE:所有的事務(wù)依次逐個執(zhí)行,這樣事務(wù)之間就完全不可能產(chǎn)生干擾悟狱,也就是說静浴,該級別可以防止臟讀、不可重復(fù)讀以及幻讀挤渐。但是這將嚴(yán)重影響程序的性能苹享。通常情況下也不會用到該級別。

指定方法:通過使用isolation屬性設(shè)置浴麻,例如:

@Transactional(isolation = Isolation.DEFAULT)

傳播行為

所謂事務(wù)的傳播行為是指得问,如果在開始當(dāng)前事務(wù)之前,一個事務(wù)上下文已經(jīng)存在白胀,此時有若干選項可以指定一個事務(wù)性方法的執(zhí)行行為椭赋。

我們可以看org.springframework.transaction.annotation.Propagation枚舉類中定義了6個表示傳播行為的枚舉值:

public enum Propagation {
    REQUIRED(0),
    SUPPORTS(1),
    MANDATORY(2),
    REQUIRES_NEW(3),
    NOT_SUPPORTED(4),
    NEVER(5),
    NESTED(6);
}
  • REQUIRED:如果當(dāng)前存在事務(wù),則加入該事務(wù)或杠;如果當(dāng)前沒有事務(wù)哪怔,則創(chuàng)建一個新的事務(wù)。
  • SUPPORTS:如果當(dāng)前存在事務(wù)向抢,則加入該事務(wù)认境;如果當(dāng)前沒有事務(wù),則以非事務(wù)的方式繼續(xù)運行挟鸠。
  • MANDATORY:如果當(dāng)前存在事務(wù)叉信,則加入該事務(wù);如果當(dāng)前沒有事務(wù)艘希,則拋出異常硼身。
  • REQUIRES_NEW:創(chuàng)建一個新的事務(wù)硅急,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起佳遂。
  • NOT_SUPPORTED:以非事務(wù)方式運行营袜,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起丑罪。
  • NEVER:以非事務(wù)方式運行荚板,如果當(dāng)前存在事務(wù),則拋出異常吩屹。
  • NESTED:如果當(dāng)前存在事務(wù)跪另,則創(chuàng)建一個事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運行;如果當(dāng)前沒有事務(wù)煤搜,則該取值等價于REQUIRED免绿。

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

@Transactional(propagation = Propagation.REQUIRED)

完整示例Chapter3-3-1

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末宅楞,一起剝皮案震驚了整個濱河市针姿,隨后出現(xiàn)的幾起案子袱吆,更是在濱河造成了極大的恐慌厌衙,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绞绒,死亡現(xiàn)場離奇詭異婶希,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蓬衡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進(jìn)店門喻杈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人狰晚,你說我怎么就攤上這事筒饰。” “怎么了壁晒?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵瓷们,是天一觀的道長。 經(jīng)常有香客問我秒咐,道長谬晕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任携取,我火速辦了婚禮攒钳,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘雷滋。我一直安慰自己不撑,他們只是感情好文兢,可當(dāng)我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著焕檬,像睡著了一般禽作。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上揩页,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天旷偿,我揣著相機(jī)與錄音,去河邊找鬼爆侣。 笑死萍程,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的兔仰。 我是一名探鬼主播茫负,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼乎赴!你這毒婦竟也來了忍法?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤榕吼,失蹤者是張志新(化名)和其女友劉穎饿序,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體羹蚣,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡原探,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了顽素。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咽弦。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖胁出,靈堂內(nèi)的尸體忽然破棺而出型型,到底是詐尸還是另有隱情,我是刑警寧澤全蝶,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布闹蒜,位于F島的核電站,受9級特大地震影響裸诽,放射性物質(zhì)發(fā)生泄漏嫂用。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一丈冬、第九天 我趴在偏房一處隱蔽的房頂上張望嘱函。 院中可真熱鬧,春花似錦埂蕊、人聲如沸往弓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽函似。三九已至槐脏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間撇寞,已是汗流浹背顿天。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留蔑担,地道東北人牌废。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像啤握,于是被迫代替她去往敵國和親鸟缕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,822評論 6 342
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理排抬,服務(wù)發(fā)現(xiàn)懂从,斷路器,智...
    卡卡羅2017閱讀 134,657評論 18 139
  • 很多人喜歡這篇文章蹲蒲,特此同步過來 由淺入深談?wù)搒pring事務(wù) 前言 這篇其實也要歸納到《常識》系列中番甩,但這重點又...
    碼農(nóng)戲碼閱讀 4,739評論 2 59
  • spring,mybatis事務(wù)管理配置與@Transactional注解使用 概述 事務(wù)管理對于企業(yè)應(yīng)用來說是至...
    tenlee閱讀 4,174評論 0 11
  • 事務(wù)有四個特性:ACID 原子性(Atomicity):事務(wù)是一個原子操作,由一系列動作組成悠鞍。事務(wù)的原子性確保動作...
    jiangmo閱讀 1,233評論 0 7