貼一篇源碼分析的好文章:https://blog.csdn.net/qq_30905661/article/details/114400417
本質(zhì):
一個事務對應一個數(shù)據(jù)庫連接。
通過 this 來調(diào)用某個帶有 @Transactional 注解的方法時,這個注解是失效的
Spring的事務是如何實現(xiàn)的?
- spring事務底層是通過數(shù)據(jù)庫事務和AOP實現(xiàn)的
- 首先對于使用@Transactional的注解的bean,spring會創(chuàng)建一個代理對象作為bean
- 當調(diào)用代理對象的方法時,spring會判斷該方法上是否加了@Transactional注解
- 如果加了,就會利用事務管理器創(chuàng)建一個數(shù)據(jù)庫連接,并修改數(shù)據(jù)庫連接的 autocommit 為 false,禁止自動提交
- 然后執(zhí)行該方法,若方法沒有拋異常則會提交事務,反之亦然
- spring事物的隔離級別就是對應數(shù)據(jù)庫的隔離級別
- spring事務的傳播機制是spring自己實現(xiàn)的,是spring事務中最復雜的
- spring事物的傳播機制是基于數(shù)據(jù)庫連接來做的,一個連接一個事務,傳播事務實際上是開了一個新的數(shù)據(jù)庫連接,在此基礎上執(zhí)行sql
Spring事物的傳播機制?
spring事務默認是注解是 REQUIRED,支持事務的傳播,使用同一個數(shù)據(jù)庫連接。
REQUIRED:spring默認的事務傳播機制,A存在事務,則B加入A的事務;A沒有事務則會新建一個數(shù)據(jù)庫事務;
SUPPORTS:支持當前事務,如果當前存在事務,就加入該事務;如果當前不存在事務,就以非事務執(zhí)行
MANDATORY:(強制性使用第一個事務)A存在事務,則B加入A的事務;A沒有事務,則拋異常
REQUIRES_NEW:創(chuàng)建一個新事務,B在這個新事務中執(zhí)行;A如果有事務將會被掛起,等待B事務方法執(zhí)行結束(commit or rollback),當B事務執(zhí)行結束后,A事務被喚醒繼續(xù)執(zhí)行,若B拋出了異常給A 或 A 方法執(zhí)行出了異常,那么在 A 事務中執(zhí)行的 sql 將會被回滾,B 事務中的sql 由B的事務管理器控制,A、B中的sql不在同一數(shù)據(jù)庫連接中執(zhí)行,即內(nèi)層事務B已經(jīng) commit 或 rollback, 外層事務干擾不了。
NOT_SUPPORTED:(不支持事務),若A存在事務,則掛起A的事務,以非事務方式運行
NEVER:(不支持事務),若A存在事務拋異常
NESTED:A存在事務,則在嵌套事務中執(zhí)行;不存在則和 REQUIRED 一樣開啟一個新事務
那些情況會導致Spring事務的失效?失效的原因是?
-
數(shù)據(jù)庫不支持事務
-
類沒有被spring管理(ioc),沒有加注解。
-
未啟用Spring事務管理功能(@EnableTransactionManagement)
-
數(shù)據(jù)源沒有配置事務管理器
@Bean public PlatformTransactionManager transactionManager(DataSource dataSource){ return new DataSourceTransactionManager(dataSource); }
-
沒有加@Configuration注解:springboot基本沒有這個問題;Spring可能會出現(xiàn)這個問題,原因是由于mybatis或JdbcTemplate會從ThreadLocal中獲取數(shù)據(jù)庫連接,但是ThreadLocal底層引用的是ThreadLocalMap,Map的key是一個DataSource對象,value是數(shù)據(jù)庫連接。如果沒有加@Configuration注解的話,會導致Map中的DataSource對象和mybatis、jdbcTenplate中的DataSource對象不相等,所有就拿不到數(shù)據(jù)庫連接,以至于自己去創(chuàng)建連接了。
-
異常被吃掉:默認情況下Spring會捕獲 error 和 RunTimeException ,spring捕獲不到異常也就不會回滾了,例如 try-catch
-
方法是private的:spring事務基于CGLIB來進行AOP,CGLIB是基于父子類來實現(xiàn),子類是代理類,子類無法重寫父類的private方法,也就沒有辦法增加spring事務邏輯。
-
方法是 final 修飾的,和private原因一致,子類不能重寫增強。
-
調(diào)用A方法和B方法不是同一個線程,不同的線程拿到的數(shù)據(jù)庫連接不一樣。TransactionSynchronizationManager.bindResource 會將線程與數(shù)據(jù)庫連接綁定。
-
rollbackFor = RuntimeException.class(默認),當拋出的異常大于定義的異常,則會導致事務失效
-
方法內(nèi)自調(diào)用時對象不是同一個:Spring事務是基于Aop,只有使用代理對象調(diào)用 A 方法時,注解才能生效,而在A方法中調(diào)用 B 方法時( this.B() ),并不是使用的代理對象,所以導致B的注解失效。
自身調(diào)用失效問題:
方法A 通過 this.B() 調(diào)用方法B。
通過 this 來調(diào)用某個帶有 @Transactional 注解的方法時,這個注解是失效的,可以看做這個方法(如上圖B)上沒有這個注解,當然書寫的傳播機制限制也是無效的,例如:propagation = Propagation.MANDATORY、propagation = Propagation.NEVER。
但是若調(diào)用A的是CGLIB生成的代理對象,并且A上有 @Transactional 注解,那么方法A是具有事務的,方法B中的sql 就在方法A的事務中執(zhí)行,所以整體A,B是有事務的。
調(diào)用使用@Transactional注解的方法時,使用的是 Spring CGLIB 創(chuàng)建的代理對象
調(diào)用B方法的是存儲在 Spring ioc容器的bean,兩個不同的對象
A調(diào)用B的結論:文章來源:http://www.zghlxwxcb.cn/news/detail-624377.html
- 只要A加@Transactional注解,A和B在不在同一個類中,B加不加@Transactional注解,事務都是有效的,則AB在同一事務中。
- A 不加 B加,A和B同一個類中:調(diào)用A方法的是CGLIB生成的代理對象,但是A方法沒有注解,所以A方法不會被攔截;this調(diào)用B,注解失效(下圖)。
- A 不加 B加,A和B不在同一個類中:不在同一個類,那么調(diào)用B的就是的就是CGLIB生成的代理對象,B的事務有效,A在外圍沒有事務(B已經(jīng)commit或rollback了,事務管理器已經(jīng)把設置auto commit = false的數(shù)據(jù)庫連接釋放了)。
文章來源地址http://www.zghlxwxcb.cn/news/detail-624377.html
到了這里,關于Spring事務傳播機制、實現(xiàn)方式、失效場景即原理的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!