Spring核心系列之Spring中的事務

我又不是架構師發表於2018-01-16

Spring核心系列之Spring中的事務

Hello,大家好,前面兩篇文章給大家分享了Spring AOP,今天就趁熱打鐵,給大家分享一下,Spring中的事務,事務這個事,其實在國內一些小公司,一般都會忽略的,尤其是很多網站,設計不到錢的系統,不會在乎這個東西,事務不回滾造成的結果無非就是髒資料,髒改等後果。因為作者以前待過的一個房產網站,根本就不在乎這個事務,有問題就有問題了,反正使用者也沒充錢在網站上。呵呵。今天還是和大家分享一下這個Spring的事務,因為這個東西算是Spring 內部使用AOP最好的一個體現,體現了AOP思想,OK,文章結構:

  1. Spring boot 中的事務
  2. Spring事務中的事務屬性

1. Spring boot 中的事務

看到Spring boot,很多人肯定感覺是被忽悠了,為什麼說講Spring事務,這又來Spring boot搞事情。用過Spring boot的小夥伴其實都知道,這兩個沒什麼大的區別,筆者這裡使用Spring boot來演示,完全是為什麼了簡便。因為搭一個Spring傳統的ssm三件套工程可能要花費5分鐘,而搭建一個Spring boot的"ssm"工程,就是滑鼠點一點的事。而且開啟事務也是一個註解的事。所以,老鐵們,對不住了,這一篇用Spring boot和大家演示Spring的事務,這裡我給一個傳送門,是傳統專案的事務,大家可以參考下:

廢話說下,直接上Spring boot程式碼:

@SpringBootApplication
@EnableTransactionManagement  
public class TestTxApplication {

	public static void main(String[] args) {
		SpringApplication.run(TestTxApplication.class, args);
	}
}
複製程式碼

@EnableTransactionManagement 表示開啟事務!

@Component
public class PersonService {
    @Autowired
    private PersonMapper personMapper;

    @Transactional
    public void testTx(){
        //該操作會成功
        personMapper.update();
        //該操作會報異常
        personMapper.update2();
    }
}
複製程式碼

這是一個Service,內部的personMapper是一個dao介面,持久化用的mybatis

@Mapper
public interface PersonMapper {
    //更改某條記錄
    @Update("update user_info set user_name='123'  where user_id='1' ")
    Long update();
    //user_info2這個表不存在。會報異常
    @Update("update user_info2 set user_name='123'  where user_id='1' ")
    Long update2();
}
複製程式碼

演示的效果應該是:

  1. 當呼叫PersonService的testTx()時,由於開啟了事務,所以update2報異常時,應該會回滾。update1的操作不會被寫入資料庫。
  2. 去掉testTx()上面的@Transactional註解,再做試驗,發現雖然報異常,但資料還是update了。

具體我就不演示了 。大家可以看到,在Spring boot中開啟事務就是一個註解的事 。具體的內部使用什麼trancationManagement根本不用管,Spring boot內部會根據pom中引入的持久層框架自動注入。真是開發神器!

然後我說下底層原理:Spring事務其實就是Spring AOP,底層建立動態代理物件,在程式碼的開頭結尾封裝了開啟事務和事務回滾操作。用過JDBC原生程式碼的更應該清楚了,都是顯示在代理裡commit和rollback的。然後一大堆try catch..

2. Spring事務中的事務屬性

上面的程式碼雖然簡單,也能應對大部分的場景,但還是有一些問題的,比如,有些異常開發者知道,並且想人為的控制,"丟擲某類異常,不要回滾".這樣的問題。這就引出了,事務屬性這個概念,事務屬性通常由事務的傳播行為,事務的隔離級別,事務的超時值和事務只讀標誌組成。 這些屬性都是在使用@Transactional可以指定的,我給一張表格:

Spring核心系列之Spring中的事務

然後把這些屬性講一講:

2.1 isolation

事務的隔離界別:使用@Transactional的Isolation屬性可以指定事務的隔離級別。但事務的隔離級別是由底層的資料庫實現的,並不是由Spring來實現。

  1. ISOLATION_DEFAULT ,這是預設的隔離級別,使用資料庫預設的事務隔離級別.另外四個與JDBC的隔離級別相對應.
  2. ISOLATION_READ_UNCOMMITTED 這是事務最低的隔離級別,它充許別外一個事務可以看到這個事務未提交的資料。這種隔離級別會產生髒讀,不可重複讀和幻像讀。
  3. ISOLATION_READ_COMMITTED 保證一個事務修改的資料提交後才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的資料。這種事務隔離級別可以避免髒讀出現,但是可能會出現不可重複讀和幻像讀。
  4. ISOLATION_REPEATABLE_READ這種事務隔離級別可以防止髒讀,不可重複讀。但是可能出現幻像讀。它除了保證一個事務不能讀取另一個事務未提交的資料外,還保證了不可重複讀。
  5. ISOLATION_SERIALIZABLE 這是花費最高代價但是最可靠的事務隔離級別。事務被處理為順序執行。除了防止髒讀,不可重複讀外,還避免了幻像讀。

一般的資料庫預設提供的是READ_COMMITTED隔離級別,如sqlserver2000;Mysql預設提供的是REPEATABLE_READ;

好了,結論給大家說完了,然後解釋一下上面提到的 髒讀,不可重複讀和幻象讀的概念。

髒讀:

  1. Mary的原工資為1000,財務人員將Mary的工資改為了8000,但未提交事務
  2. 與此同時,Mary正在讀取自己的工資.Mary發現自己的工資變為了8000,歡天喜地! (髒讀)
  3. 而財務發現操作有誤,而回滾了事務,Mary的工資又變為了1000.

不可重複讀:在一個事務中前後兩次讀取的結果並不致,導致了不可重複讀。

  1. 在事務1中,Mary 讀取了自己的工資為1000,操作並沒有完成 .
  2. 在事務2中,這時財務人員修改了Mary的工資為2000,並提交了事務.
  3. 在事務1中,Mary 再次讀取自己的工資時,工資變為了2000.

幻想讀:

  1. 目前工資為1000的員工有10人。
  2. 事務1,讀取所有工資為1000的員工。共讀取10條記錄 .
  3. 這時另一個事務向employee表插入了一條員工記錄,工資也為1000
  4. 事務1再次讀取所有工資為1000的員工,共讀取到了11條記錄,這就產生了幻像讀。

好了,事務的隔離級別就講完了,一般還是採用資料庫預設的,像mysql的REPEATABLE_READ,能夠避免髒讀和不可重複讀。

2.2 propagation

  1. REQUIRED:表示業務方法需要在一個事務中處理,如果業務方法執行時已經在一個事務中,則加入該事務,否則重新開啟一個事務。這也是預設的事務傳播行為;
  2. NOT_SUPPORTED:宣告業務方法不需要事務,如果業務方法執行時已經在一個事務中,則事務被掛起,等方法執行完畢後,事務恢復進行;

其他的傳播行為省略,基本不用,一般的傳播行為也是使用預設的:REQUIRED

2.3 rollbackFor rollbackForClassName noRollbackFor norollbackForClassName

Transactional的異常控制,預設是Check Exception不回滾,unCheck Exception回滾,rollbackFor 和noRollbackFor 配置也許不會含蓋所有異常,對於遺漏的按照Check Exception 不回滾,unCheck Exception回滾.

使用@Transactional註解的noRollbackFor和rollbackFor屬性可以改變預設的行為:

  • 如:@Transactional(rollbackFor=Exception.class)可以使checked異常發生時,資料庫操作也rollback,@Transactional(noRollbackFor=RuntimeException.class)可以使unchecked異常發生時也提交資料庫操作。

也可以使用noRollbackForClassName、rollbackForClassName屬性來指定一個異常類名的String陣列來改變預設的行為。

好了,事務屬性大致就這麼多了,其他一些就是比較簡單的了,看名字就知道啥意思,伸手就用的東西,然後再提一下,@Transactional只能被應用到public方法上, 對於其它非public的方法,如果標記了@Transactional也不會報錯,但方法沒有事務功能..

老實說,一般事務屬性配置的比較少,一般都是直接@Transactional加上就完事了。很少配置一些屬性。尤其是那個事務傳播行為,在日常的業務裡,很少有食物巢狀的情況,所以我就是點了一點,沒有展開。一般使用預設就可以了。

結語

好了,Spring的事務使用層面給大家算是分享完了,在Spring boot裡想使用事務簡單的有點可怕。所以我就採用Spring boot了。其實傳統的SSM專案也很簡單,無非就是在xml檔案裡配置下tranclationManagement..然後Spring 事務的底層是Spring AOP,把jdbc的事務程式碼嵌入了進去。Over,Have a good day!

相關文章