Spring詳細(xì)解讀事務(wù)管理
什么是事務(wù)
事務(wù)就是對(duì)數(shù)據(jù)庫(kù)若干操作組成的一個(gè)單元
我們?cè)陂_(kāi)發(fā)企業(yè)應(yīng)用時(shí),對(duì)于業(yè)務(wù)人員的一個(gè)操作實(shí)際是對(duì)數(shù)據(jù)讀寫的多步操作的結(jié)合。由于數(shù)據(jù)操作在順序執(zhí)行的過(guò)程中,任何一步操作都有可能發(fā)生異常,異常會(huì)導(dǎo)致后續(xù)操作無(wú)法完成,此時(shí)由于業(yè)務(wù)邏輯并未正確的完成,之前成功操作數(shù)據(jù)的并不可靠,需要在這種情況下進(jìn)行回退
事務(wù)的作用就是為了保證用戶的每一個(gè)操作都是可靠的,事務(wù)中的每一步操作都必須成功執(zhí)行,只要有發(fā)生異常就回退到事務(wù)開(kāi)始未進(jìn)行操作的狀態(tài),這些操作要么都完成,要么都取消,從而保證數(shù)據(jù)滿足一致性的要求
如何理解呢 ?
例如 : A現(xiàn)在要轉(zhuǎn)賬給B 那么轉(zhuǎn)賬是幾個(gè)方法呢? 兩個(gè) : 方法1: A 減錢 方法2: B加錢
如果A方法成功執(zhí)行后 , B方法中執(zhí)行時(shí)出現(xiàn)了異常, 就等于A錢扣了卻沒(méi)有給B加錢, 那么這樣的行為肯定是不允許的, 所以我們引入事務(wù)的概念 , 事務(wù)一般也是對(duì)于數(shù)據(jù)庫(kù)而言的
Spring事務(wù)配置
Spring事務(wù)管理又分為編程式事務(wù)和聲明式事務(wù)
編 程 式 事 務(wù) 在 項(xiàng) 目 中 很 少 使 用 , 這 種 方 式 需 要 注 入 一 個(gè) 事 務(wù) 管 理 對(duì) 象 TransactionTemplate ,然后在我們代碼中需要提交事務(wù)或回滾事務(wù)時(shí)自己寫代碼實(shí)現(xiàn)
聲明式事務(wù)管理建立在 AOP 基礎(chǔ)上,本質(zhì)是對(duì)方法前后進(jìn)行攔截,所以聲明式事務(wù)是方法級(jí)別的。
為什么說(shuō)是基于AOP呢? 因?yàn)樵谕ㄟ^(guò)xml文件配置中我們是這樣來(lái)做的
<aop:config> <aop:pointcut expression="execution(* com.ff.spring.service.UserService.*(..))" id="allmethod"/> <aop:advisor advice-ref="txadvice" pointcut-ref="allmethod"/> </aop:config>
基于注解的方式直接幫我們省略了這個(gè)過(guò)程, 更為方便
這里我們主要介紹聲明式事務(wù)
首先在db.xml中配置spring事務(wù)管理類
<!--配置spring事務(wù)管理類--> <bean id="sourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="druidDataSource"/> </bean>
這里我們直接介紹基于注解方式的spring事務(wù)管理
開(kāi)啟注解掃描
<!-- 開(kāi)啟注解事務(wù)管理 --> <tx:annotation-driven transaction-manager="sourcetransactionManager"/>
我們使用@Transactiona 這樣一個(gè)注解標(biāo)簽來(lái)聲明事務(wù), 它可以作用于方法,表明這個(gè)方法支持事務(wù), 也可以作用于類, 表明這個(gè)類中的所有方法支持事務(wù)
public class UserDao { @Autowired JdbcTemplate jdbcTemplate; @Transactional(propagation= Propagation.REQUIRED) public void saveUser(){ jdbcTemplate.update("insert into admin(account,pass_word,sex) values (?,?,?)","li","111","男"); int i=108/0; //出現(xiàn)異常 jdbcTemplate.update("insert into admin(account,pass_word,sex) values (?,?,?)","qw","111","男"); } }
在上述代碼中, 第一條sql 語(yǔ)句雖然成功執(zhí)行, 但后面出現(xiàn)了異常, 所以這個(gè)方法的事務(wù)并沒(méi)有提交, 是不會(huì)向數(shù)據(jù)庫(kù)提交數(shù)據(jù)的
Spring事務(wù)傳播行為
即然是傳播,那么至少有兩個(gè)東西,才可以發(fā)生傳播。單體不存在傳播這個(gè)行為。事務(wù)傳播行為(propagation behavior)指的就是當(dāng)一個(gè)事務(wù)方法被另一個(gè)事務(wù)方法調(diào)用時(shí),這個(gè)事務(wù)方法應(yīng)該如何進(jìn)行。事務(wù)傳播行為是 Spring 框架獨(dú)有的事務(wù)增強(qiáng)特性,他不屬于的事務(wù)實(shí)際提供方數(shù)據(jù)庫(kù)行為.
是不是有點(diǎn)懵, 說(shuō)啥呢這是, 接著來(lái)解釋
試想 , 有兩個(gè)方法 A 和 B , 它們都有事務(wù), 那么我在 A 方法中去調(diào)用 B 方法, 那么B方法此時(shí)
應(yīng)該怎樣去執(zhí)行呢? 是將B 方法加入到 A 方法組成一個(gè)事務(wù),還是它們都是一個(gè)獨(dú)立的事務(wù)呢 ?
Spring定義了 7種 事務(wù)傳播行為, 我們下面主要介紹其中三種,都會(huì)舉例說(shuō)明
傳播行為一般是對(duì)于B方法(被調(diào)用的方法而言的)
1. PROPAGATION_REQUIRED
指定的方法必須在事務(wù)內(nèi)執(zhí)行,若當(dāng)前存在事務(wù),加入到當(dāng)前事務(wù)中,若當(dāng)前沒(méi)有事務(wù),則創(chuàng)建一個(gè)新事務(wù),這種傳播行為是最常見(jiàn)的,也是 spring 默認(rèn)的傳播行為
A 方法調(diào)用 B方法(PROPAGATION_REQUIRED) , 如果A方法是存在事務(wù)的, 那么直接將B方
法加入到 A事務(wù)中,組成一個(gè)事務(wù). 如果 A 方法是沒(méi)有事務(wù)的, 那么B就是一個(gè)單獨(dú)的事務(wù)
這么說(shuō)可能有點(diǎn)繞 , 我們?cè)俅握?qǐng)出李雷, 算了, 這次就放過(guò)李雷, 換成張三吧
就是說(shuō)呢 , 我和李雷現(xiàn)在都要去吃飯, 然后我說(shuō)呢,李雷咱倆一起吧(A調(diào)用B,我叫李雷去吃飯) , 不出意外的話(事務(wù)順利執(zhí)行提交), 我們兩吃飯作為一個(gè)整體, 最終都吃了飯(AB都存在事務(wù)就作為一個(gè)整體事務(wù)) , 但是還有一種情況, 我叫李雷去吃飯, 但是我卻沒(méi)有去(A調(diào)用B, 但是A沒(méi)有事務(wù)),那么這時(shí)候李雷肯定單獨(dú)去吃飯(B單獨(dú)開(kāi)啟一個(gè)事務(wù))
@Transactional(propagation = Propagation.REQUIRED) public void saveDept(){ //A調(diào)用B deptDao.saveDept(); commonService.saveLog(); //B }
@Transactional(propagation = Propagation.REQUIRED) public void saveLog(){ commonDao.saveLog(); int a = 10/0; //異常 }
此時(shí) A 調(diào)用 B , 它們都有事務(wù), B中出了異常, 那么此時(shí)AB最終都不會(huì)去和數(shù)據(jù)庫(kù)交互
//@Transactional(propagation = Propagation.REQUIRED) public void saveDept(){ //A調(diào)用B deptDao.saveDept(); commonService.saveLog(); //B }
@Transactional(propagation = Propagation.REQUIRED) public void saveLog(){ commonDao.saveLog(); int a = 10/0; //異常 }
第二種方式 , 我們將異常加到A 中, 注釋掉注解(取消A的事務(wù)), 那么此時(shí)B是單獨(dú)的一個(gè)事務(wù), B里面出了異常, 不會(huì)去和數(shù)據(jù)庫(kù)交互, 則A會(huì)去和數(shù)據(jù)庫(kù)交互
2. PROPAGATION_SUPPORTS
支持當(dāng)前事務(wù),如果當(dāng)前沒(méi)有事務(wù),就以非事務(wù)方式執(zhí)行
A方法調(diào)用B方法(PROPAGATION_SUPPORTS), 如果A是存在事務(wù)的,那么直接將B方
法加入到 A事務(wù)中,組成一個(gè)事務(wù). 如果 A 沒(méi)有事務(wù), 那么B也沒(méi)有事務(wù)
我叫李雷去吃飯 , 如果我一定要去吃飯(A調(diào)用B), 那么最終我吃飯和李雷吃飯就總共作為一個(gè)事務(wù),就和上面第一個(gè)例子是一樣的, 作為整體的一個(gè)事務(wù). 但是第二種情況就是, 我雖然叫了李雷去吃飯, 但是我最終沒(méi)有去(A沒(méi)有事務(wù)) , 這時(shí)候李雷說(shuō), 那我也不去吃飯了(B也沒(méi)有事務(wù))
@Transactional(propagation = Propagation.REQUIRED) public void saveDept(){ //A調(diào)用B deptDao.saveDept(); commonService.saveLog(); //B }
@Transactional(propagation = Propagation.SUPPORTS) public void saveLog(){ commonDao.saveLog(); int a = 10/0; //異常 }
此時(shí) A調(diào)用 B , AB都有事務(wù), 那么B 中出現(xiàn)異常, 最終都不會(huì)和數(shù)據(jù)庫(kù)交互,就和上述第一種情況
一樣
//@Transactional(propagation = Propagation.REQUIRED) public void saveDept(){ //A調(diào)用B deptDao.saveDept(); commonService.saveLog(); //B }
取消掉A 的事務(wù) , 那么此時(shí)B 會(huì)以非事務(wù)執(zhí)行 , 這時(shí)候AB都會(huì)和數(shù)據(jù)庫(kù)去交互,因?yàn)榉鞘聞?wù)
3. PROPAGATION_REQUIRES_NEW
總是新建一個(gè)事務(wù),如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起,直到新建的事務(wù)結(jié)束。
A方法調(diào)用B方法(PROPAGATION_REQUIRES_NEW), 如果A存在事務(wù), 那么B此時(shí)會(huì)先把A事務(wù)掛起, 然后為自己新建一個(gè)事務(wù), 先執(zhí)行完B事務(wù), 才會(huì)去執(zhí)行 A 事務(wù) . 如果A 沒(méi)有事務(wù), 那么B自己?jiǎn)为?dú)新建一個(gè)事務(wù)執(zhí)行
這里就是, 我雖然叫李雷去吃飯(A調(diào)用B) , 李雷都會(huì)先自己去吃飯, 不等我(B自己新建一個(gè)事務(wù), A有事務(wù)先將A掛起)
@Transactional(propagation = Propagation.REQUIRED) public void saveDept(){ //A調(diào)用B deptDao.saveDept(); commonService.saveLog(); //B }
@Transactional(propagation = Propagation.REQUIRES_NEW) public void saveLog(){ commonDao.saveLog(); int a = 10/0; //異常 }
A 調(diào)用 B , A支持事務(wù) , 此時(shí)B將A事務(wù)掛起, 單獨(dú)開(kāi)啟一個(gè)事務(wù),里面出現(xiàn)異常,此時(shí)AB都不會(huì)和數(shù)據(jù)庫(kù)交互
//@Transactional(propagation = Propagation.REQUIRED) public void saveDept(){ //A調(diào)用B deptDao.saveDept(); commonService.saveLog(); //B }
取消A的事務(wù), 但此時(shí)B事務(wù)是獨(dú)立的, 出現(xiàn)異常, 所以B不會(huì)和數(shù)據(jù)庫(kù)交互,但是A和數(shù)據(jù)庫(kù)交互
聲明式事務(wù)失效
事務(wù)也是會(huì)失效的 , 失效有以下幾種情況
1.@Transactional 應(yīng)用在非 public 修飾的方法上
2.@Transactional 注解屬性 propagation 設(shè)置錯(cuò)誤
3.同一個(gè)類中方法調(diào)用,導(dǎo)致@Transactional 失效
4.異常被 catch 捕獲導(dǎo)致@Transactional 失效
5.數(shù)據(jù)庫(kù)引擎不支持事務(wù)
非public修飾導(dǎo)致權(quán)限錯(cuò)誤, 事務(wù)失效, propagation設(shè)置參數(shù)錯(cuò)誤導(dǎo)致事務(wù)失效
同類下方法調(diào)用也會(huì)導(dǎo)致事務(wù)失效
catch如果捕獲了異常, 就相當(dāng)于程序沒(méi)有異常,這時(shí)事務(wù)也會(huì)失效
最后就是數(shù)據(jù)庫(kù)引擎不支持, mysql中只有InnoDB引擎是支持事務(wù)的
結(jié)語(yǔ)
關(guān)于Spring 事務(wù)管理就先說(shuō)到這 , 后面介紹SpringMVC 與 ssm 框架, 謝謝,愛(ài)你們??學(xué) spring 一定要笑, 笑著學(xué) ,嘿嘿
到此這篇關(guān)于Spring詳細(xì)解讀事務(wù)管理的文章就介紹到這了,更多相關(guān)Spring事務(wù)管理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
深入淺析Spring Security5中默認(rèn)密碼編碼器
這篇文章主要介紹了Spring Security5中默認(rèn)密碼編碼器,非常不錯(cuò),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2019-05-05Springboot下使用Redis管道(pipeline)進(jìn)行批量操作
本文主要介紹了Spring?boot?下使用Redis管道(pipeline)進(jìn)行批量操作,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05Mybatis Plus 代碼生成器的實(shí)現(xiàn)
這篇文章主要介紹了Mybatis Plus 代碼生成器的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09HDFS?Balancer負(fù)載均衡器及語(yǔ)法詳解
這篇文章主要為大家介紹了HDFS?Balancer負(fù)載均衡器及語(yǔ)法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03