老生常談spring的事務(wù)傳播機(jī)制
spring的事務(wù)傳播機(jī)制
背景:實(shí)習(xí)期間幾次遇到事務(wù)方法,有一次本地測試時(shí)發(fā)現(xiàn)事務(wù)沒有回滾,就把簡單描述寫在wx上,今天來給spring事務(wù)做個(gè)自我總結(jié)。
1、why
為什么會(huì)有事務(wù)傳播機(jī)制?
場景一:
- serviceA 方法調(diào)用了 serviceB 方法,但兩個(gè)方法都有事務(wù),這個(gè)時(shí)候如果 serviceB 方法異常,是讓 serviceB 方法提交,還是兩個(gè)一起回滾。
場景二:
- serviceA 方法調(diào)用了 serviceB 方法,但是只有 serviceA 方法加了事務(wù),是否把 serviceB 也加入 serviceA 的事務(wù),如果 serviceB 異常,是否回滾 serviceA 。
場景三:
- serviceA 方法調(diào)用了 serviceB 方法,兩者都有事務(wù),serviceB 已經(jīng)正常執(zhí)行完,但 serviceA 異常,是否需要回滾 serviceB 的數(shù)據(jù)。
所以,我們需要有對應(yīng)的事務(wù)傳播機(jī)制來控制事務(wù)。
2、傳播機(jī)制生效的條件
有了spring事務(wù)傳播機(jī)制,那這種機(jī)制存在的條件呢?我們知道,spring的事務(wù)是基于aop的,確切來說,是基于JDK動(dòng)態(tài)代理的AOP,這種AOP有什么特點(diǎn)呢? 它是基于類或者接口的,也就是說,當(dāng) @Transactional寫在一個(gè)方法上時(shí),這個(gè)方法將會(huì)被spring動(dòng)態(tài)代理, 生成一個(gè)動(dòng)態(tài)代理類, 對原方法進(jìn)行修飾增強(qiáng),但是要注意!! 原先的方法的類并沒有什么不同,并沒有事務(wù),spring動(dòng)態(tài)代理這個(gè)類生成的代理類才有事務(wù),才有增強(qiáng),也就是說,在同一個(gè)類里面通過this.xx()調(diào)用本類的事務(wù)方法時(shí),事務(wù)是不會(huì)生效的,因?yàn)槟阏{(diào)用的不是代理類。
@Transactional @Override public void method1() { this.method2(); } @Transactional(propagation = Propagation.REQUIRES_NEW) @Override public void method2() { xx }
解決方案
關(guān)鍵在于獲取類的代理對象,而不是通過this去調(diào)用,所以以下方法都是基于這個(gè)關(guān)鍵點(diǎn)去解決的。
- 1、最簡單的,兩個(gè)事務(wù)方法放在不同的service里面,這個(gè)比較簡單,就不給例子了。(推薦)
- 2、AOP上下文。spring提供了AOP上下文AopContext,因此通過AopContext,可以很方便的獲取到代理對象。
public class Myservice{ @Transactional @Override public void method1() { ((Myservice)AopContext.currentProxy()).method2(); } @Transactional(propagation = Propagation.REQUIRES_NEW) @Override public void method2() { xx } }
一運(yùn)行,報(bào)錯(cuò)了,因?yàn)閑xposeProxy默認(rèn)為false,我們要暴露代理類,就要設(shè)置為true,可以在springboot啟動(dòng)類上加一個(gè)注解
@EnableAspectJAutoProxy(exposeProxy = true)
- 3、ApplicationContext
public class Myservice{ @Autowired ApplicationContext context; Myservice service; @PostConstruct //初始化時(shí)調(diào)用,不加也行 private void getMyservice() { service = context.getBean(Myservice.class); } @Transactional @Override public void method1() { service.method2(); } @Transactional(propagation = Propagation.REQUIRES_NEW) @Override public void method2() { xx } }
第二和第三種的區(qū)別就在于,2是直接獲取代理類,3是通過調(diào)用getBean間接獲取代理類,總的來說,第一種是最方便的,也是最推薦的做法。
3、傳播機(jī)制類型
下面的類型都是針對于被調(diào)用方法來說的,理解起來要想象成兩個(gè) service 方法的調(diào)用才可以。
PROPAGATION_REQUIRED (默認(rèn))
- 支持當(dāng)前事務(wù),如果當(dāng)前沒有事務(wù),則新建事務(wù)
- 如果當(dāng)前存在事務(wù),則加入當(dāng)前事務(wù),合并成一個(gè)事務(wù)
REQUIRES_NEW (一般用在子方法需要單獨(dú)事務(wù))
- 新建事務(wù),如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起
- 這個(gè)方法會(huì)獨(dú)立提交事務(wù),不受調(diào)用者的事務(wù)影響,父級(jí)異常,它也是正常提交
- (上面兩個(gè)類型是常用的,下面的比較少用)
NESTED
- 如果當(dāng)前存在事務(wù),它將會(huì)成為父級(jí)事務(wù)的一個(gè)子事務(wù),方法結(jié)束后并沒有提交,只有等父事務(wù)結(jié)束才提交
- 如果當(dāng)前沒有事務(wù),則新建事務(wù)
- 如果它異常,父級(jí)可以捕獲它的異常而不進(jìn)行回滾,正常提交
- 但如果父級(jí)異常,它必然回滾,這就是和 REQUIRES_NEW 的區(qū)別
SUPPORTS
- 如果當(dāng)前存在事務(wù),則加入事務(wù)
- 如果當(dāng)前不存在事務(wù),則以非事務(wù)方式運(yùn)行,這個(gè)和不寫沒區(qū)別
NOT_SUPPORTED
- 以非事務(wù)方式運(yùn)行
- 如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起
MANDATORY
- 如果當(dāng)前存在事務(wù),則運(yùn)行在當(dāng)前事務(wù)中
- 如果當(dāng)前無事務(wù),則拋出異常,也即父級(jí)方法必須有事務(wù)
NEVER
- 以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則拋出異常,即父級(jí)方法必須無事務(wù)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
eclipse如何搭建Springboot項(xiàng)目詳解
今天帶大家學(xué)習(xí)eclipse如何搭建Spring boot項(xiàng)目,文中有非常詳細(xì)的圖文解說,對正在學(xué)習(xí)java的小伙伴們有很好地幫助,需要的朋友可以參考下2021-05-05Spring與Mybatis相結(jié)合實(shí)現(xiàn)多數(shù)據(jù)源切換功能
這篇文章主要介紹了Spring與Mybatis相結(jié)合實(shí)現(xiàn)多數(shù)據(jù)源切換功能的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-06-06java基于Apache FTP實(shí)現(xiàn)文件上傳、下載、修改文件名、刪除
本篇文章主要介紹了Apache FTP實(shí)現(xiàn)文件上傳、下載、修改文件名、刪除,實(shí)現(xiàn)了FTP文件上傳(斷點(diǎn)續(xù)傳)、FTP文件下載、FTP文件重命名、FTP文件刪除等功能,有需要的可以了解一下。2016-11-11Java計(jì)算兩個(gè)時(shí)間段的差的實(shí)例詳解
在本篇內(nèi)容中,我們給大家整理了關(guān)于Java計(jì)算兩個(gè)時(shí)間段的差的實(shí)例內(nèi)容,并做了詳細(xì)分析,有需要的朋友們學(xué)習(xí)下。2022-11-11Java多線程連續(xù)打印abc實(shí)現(xiàn)方法詳解
這篇文章主要介紹了Java多線程連續(xù)打印abc實(shí)現(xiàn)方法詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03