Spring Boot中的事務管理

什么是事務?

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

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

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

快速入門

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

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

在該樣例工程中(若對該數(shù)據(jù)訪問方式不了解,可先閱讀該文章),我們引入了spring-data-jpa,并創(chuàng)建了User實體以及對User的數(shù)據(jù)訪問對象UserRepository,在ApplicationTest類中實現(xiàn)了使用UserRepository進行數(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)藶榈膩碇圃煲恍┊惓?,看看會發(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;

    // 省略構造函數(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ā)生異常的時候被回退,這時候就可以使用事務讓它實現(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ù)需要被事務管理,通常我們單元測試為了保證每個測試之間的數(shù)據(jù)獨立,會使用@Rollback注解讓每個單元測試都能在結束時回滾。而真正在開發(fā)業(yè)務邏輯時,我們通常在service層接口中使用@Transactional來對各個業(yè)務邏輯進行事務管理的配置,例如:


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

事務詳解

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

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

隔離級別

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

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

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

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

@Transactional(isolation = Isolation.DEFAULT)

傳播行為

所謂事務的傳播行為是指,如果在開始當前事務之前,一個事務上下文已經(jīng)存在,此時有若干選項可以指定一個事務性方法的執(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:如果當前存在事務,則加入該事務;如果當前沒有事務,則創(chuàng)建一個新的事務。
  • SUPPORTS:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續(xù)運行。
  • MANDATORY:如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。
  • REQUIRES_NEW:創(chuàng)建一個新的事務,如果當前存在事務,則把當前事務掛起。
  • NOT_SUPPORTED:以非事務方式運行,如果當前存在事務,則把當前事務掛起。
  • NEVER:以非事務方式運行,如果當前存在事務,則拋出異常。
  • NESTED:如果當前存在事務,則創(chuàng)建一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價于REQUIRED。

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

@Transactional(propagation = Propagation.REQUIRED)

完整示例Chapter3-3-1

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容