一、@Transactional
在使用Spring框架時(shí)璧函,可以有兩種使用事務(wù)的方式,一種是編程式的基显,一種是申明式的蘸吓,@Transactional注解就是申明式的。首先撩幽,事務(wù)這個(gè)概念是數(shù)據(jù)庫(kù)層面的库继,Spring只是基于數(shù)據(jù)庫(kù)中的事務(wù)進(jìn)行了擴(kuò)展,比如我們可以通過在某個(gè)方法上增加@Transactional注解窜醉,就可以開啟事務(wù)宪萄,這個(gè)方法中所有的sql都會(huì)在一個(gè)事務(wù)中執(zhí)行,統(tǒng)一成功或失敗榨惰。在一個(gè)方法上加了@Transactional注解后拜英,Spring會(huì)基于這個(gè)類生成一個(gè)
代理對(duì)象
,會(huì)將這個(gè)代理對(duì)象作為bean琅催,當(dāng)在使用這個(gè)代理對(duì)象的方法時(shí)居凶,如果這個(gè)方法上存在@Transactional注解,那么代理邏輯會(huì)先把事務(wù)的自動(dòng)提交設(shè)置為false恢暖,然后再去執(zhí)行原本的業(yè)務(wù)邏輯方法排监,如果執(zhí)行業(yè)務(wù)邏輯方法沒有
出現(xiàn)異常,那么代理邏輯中就會(huì)將事務(wù)進(jìn)行提交杰捂,如果執(zhí)行業(yè)務(wù)邏輯方法出現(xiàn)了異常舆床,那么則會(huì)將事務(wù)進(jìn)行回滾。當(dāng)然嫁佳,針對(duì)哪些異嘲ざ樱回滾事務(wù)是可以配置的,可以利用@Transactional注解中的rollbackFor屬性進(jìn)行配置蒿往,默認(rèn)情況下會(huì)對(duì)RuntimeException和Error進(jìn)行回滾盛垦。
二、事務(wù)的隔離級(jí)別
一個(gè)事務(wù)與其他事務(wù)的隔離的程度成為隔離級(jí)別瓤漏,以下是四種隔離級(jí)別腾夯,依次是變高的颊埃,隔離級(jí)別越高,數(shù)據(jù)的一致性就越好蝶俱,但也不是越高就越好班利,因?yàn)樵礁邔?duì)于性能的花銷就越大。開發(fā)中常用的隔離級(jí)別是讀已提交榨呆。以下是一些并發(fā)事務(wù)常見問題:
事務(wù)的四大特性分別是:原子性罗标、一致性、隔離性积蜻、持久性
幻讀和不可重復(fù)讀都是在同一個(gè)事務(wù)中多次讀取了其他事務(wù)已經(jīng)提交的事務(wù)的數(shù)據(jù)導(dǎo)致每次讀取的數(shù)據(jù)不一致闯割,所不同的是不可重復(fù)讀讀取的是同一條數(shù)據(jù),而幻讀針對(duì)的是一批數(shù)據(jù)整體的統(tǒng)計(jì)(比如數(shù)據(jù)的個(gè)數(shù))
1.讀未提交:READ UNCOMMITTED(可以讀到未提交的竿拆,可能會(huì)發(fā)生臟讀)
2.讀已提交:READ CONNITTED(可以避免臟讀)
3.可重復(fù)讀:REPEATABLE READ(可以避免不可重復(fù)讀)
4.串行化:SERIALIZABLE(可以避免幻讀)
以MYSQL數(shù)據(jù)庫(kù)來(lái)分析四種隔離級(jí)別
第一種隔離級(jí)別:Read uncommitted(讀未提交)
如果一個(gè)事務(wù)已經(jīng)開始寫數(shù)據(jù)宙拉,則另外一個(gè)事務(wù)不允許同時(shí)進(jìn)行寫操作,但允許其他事務(wù)讀此行數(shù)據(jù)如输,該隔離級(jí)別可以通過“排他寫鎖”鼓黔,但是不排斥讀線程實(shí)現(xiàn)央勒。這樣就避免了更新丟失不见,卻可能出現(xiàn)臟讀,也就是說事務(wù)B讀取到了事務(wù)A未提交的數(shù)據(jù)
同一個(gè)數(shù)據(jù)崔步,一個(gè)事務(wù)寫稳吮,另一個(gè)事務(wù)可以讀。
解決了更新丟失井濒,但還是可能會(huì)出現(xiàn)臟讀
第二種隔離級(jí)別:Read committed(讀提交)
如果是一個(gè)讀事務(wù)(線程)灶似,則允許其他事務(wù)讀寫,如果是寫事務(wù)將會(huì)禁止其他事務(wù)訪問該行數(shù)據(jù)瑞你,該隔離級(jí)別避免了臟讀酪惭,但是可能出現(xiàn)不可重復(fù)讀。事務(wù)A事先讀取了數(shù)據(jù)者甲,事務(wù)B緊接著更新了數(shù)據(jù)春感,并提交了事務(wù),而事務(wù)A再次讀取該數(shù)據(jù)時(shí)虏缸,數(shù)據(jù)已經(jīng)發(fā)生了改變鲫懒。
同一個(gè)數(shù)據(jù),一個(gè)事務(wù)寫刽辙,另一個(gè)事務(wù)不可以讀窥岩。一個(gè)事務(wù)讀,另一個(gè)事務(wù)可以讀寫宰缤。
解決了更新丟失和臟讀問題
第三種隔離級(jí)別:Repeatable read(可重復(fù)讀取)
可重復(fù)讀取是指在一個(gè)事務(wù)內(nèi)颂翼,多次讀同一個(gè)數(shù)據(jù)晃洒,在這個(gè)事務(wù)還沒結(jié)束時(shí),其他事務(wù)不能訪問該數(shù)據(jù)(包括了讀寫)朦乏,這樣就可以在同一個(gè)事務(wù)內(nèi)兩次讀到的數(shù)據(jù)是一樣的锥累,因此稱為是可重復(fù)讀隔離級(jí)別,讀取數(shù)據(jù)的事務(wù)將會(huì)禁止寫事務(wù)(但允許讀事務(wù))集歇,寫事務(wù)則禁止任何其他事務(wù)(包括了讀寫)桶略,這樣避免了不可重復(fù)讀和臟讀,但是有時(shí)可能會(huì)出現(xiàn)幻讀诲宇。(讀取數(shù)據(jù)的事務(wù))可以通過“共享讀鎖”和“排他寫鎖”實(shí)現(xiàn)际歼。
//行級(jí)排他鎖
select * from test for update
//行級(jí)共享鎖,注意可能會(huì)發(fā)生死鎖
select * from test lock in share mode
同一個(gè)數(shù)據(jù),一個(gè)事務(wù)讀姑蓝,另一個(gè)事務(wù)可以讀鹅心,一個(gè)事務(wù)寫,另一個(gè)事務(wù)既不可以讀也不可以寫纺荧。
解決了更新丟失旭愧、臟讀、不可重復(fù)讀宙暇、但是還會(huì)出現(xiàn)幻讀
第四種隔離級(jí)別:Serializable(可序化)
提供嚴(yán)格的事務(wù)隔離输枯,它要求事務(wù)序列化執(zhí)行,事務(wù)只能一個(gè)接著一個(gè)地執(zhí)行占贫,但不能并發(fā)執(zhí)行桃熄,如果僅僅通過“行級(jí)鎖”是無(wú)法實(shí)現(xiàn)序列化的,必須通過其他機(jī)制保證新插入的數(shù)據(jù)不會(huì)被執(zhí)行查詢操作的事務(wù)訪問到型奥。序列化是最高的事務(wù)隔離級(jí)別瞳收,同時(shí)代價(jià)也是最高的,性能很低厢汹,一般很少使用螟深,在該級(jí)別下,事務(wù)順序執(zhí)行烫葬,不僅可以避免臟讀界弧、不可重復(fù)讀,還避免了幻讀
解決了更新丟失厘灼、臟讀夹纫、不可重復(fù)讀、幻讀(虛讀)
如果數(shù)據(jù)庫(kù)的配置隔離級(jí)別是Read Commited设凹,而Spring配置的隔離級(jí)別是Repeatable Read舰讹,這時(shí)候以Spring配置的為準(zhǔn),如果Spring設(shè)置的隔離級(jí)別數(shù)據(jù)庫(kù)不支持闪朱,則效果取決于數(shù)據(jù)庫(kù)月匣。
mysql默認(rèn)的事務(wù)處理級(jí)別是'REPEATABLE-READ',也就是可重復(fù)讀
Oracle默認(rèn)系統(tǒng)事務(wù)隔離級(jí)別是READ COMMITTED,也就是讀已提交
SQL Server默認(rèn)系統(tǒng)事務(wù)隔離級(jí)別是READ COMMITTED,也就是讀已提交钻洒。
三、spring事務(wù)傳播機(jī)制
spring事務(wù)傳播機(jī)制是指锄开,多個(gè)事務(wù)方法相互調(diào)用時(shí)素标,事務(wù)如何在這些方法間傳播。方法A是一個(gè)事務(wù)的方法萍悴,方法A執(zhí)行過程中調(diào)用了方法B头遭,那么方法B有無(wú)事務(wù)以及方法B對(duì)事務(wù)的要求不同都會(huì)對(duì)方法A的事務(wù)具體執(zhí)行造成影響,同時(shí)方法A的事務(wù)對(duì)方法B的事務(wù)執(zhí)行也有影響癣诱,這種影響具體是什么由兩個(gè)方法所定義的事務(wù)傳播類型決定计维。
NESTED的重點(diǎn)是作為調(diào)用方的一個(gè)嵌套。
NESTED和REQUIRES_NEW的區(qū)別:REQUIRES_NEW是新建一個(gè)事務(wù)并且新開啟的這個(gè)事務(wù)與原有事務(wù)無(wú)關(guān)撕予,而NESTED則是當(dāng)前存在事務(wù)時(shí)(我們把當(dāng)前事務(wù)稱之為父事務(wù))會(huì)開啟一個(gè)嵌套事務(wù)(稱之為一個(gè)子事務(wù))鲫惶。 在NESTED情況下父事務(wù)回滾時(shí),子事務(wù)也會(huì)回滾实抡,而在REQUIRES_NEW情況下欠母,原有事務(wù)回滾,不會(huì)影響新開啟的事務(wù)吆寨。
NESTED和REQUIRED的區(qū)別:REQUIRED情況下赏淌,調(diào)用方存在事務(wù)時(shí),則被調(diào)用方和調(diào)用方使用同一事務(wù)鸟废,那么被調(diào)用方出現(xiàn)異常時(shí)猜敢,由于共用一個(gè)事務(wù),所以無(wú)論調(diào)用方是否catch其異常盒延,事務(wù)都會(huì)回滾,而在NESTED情況下鼠冕,被調(diào)用方發(fā)生異常時(shí)添寺,調(diào)用方可以catch其異常,這樣只有子事務(wù)回滾懈费,父事務(wù)不受影響计露。
事務(wù)的傳播屬性通過@Transactional注解中對(duì)屬性propagation=" "來(lái)進(jìn)行配置,默認(rèn)是“REQUIRED”是運(yùn)行原來(lái)的事務(wù)憎乙,還有一個(gè)常用的是"REQUIRED_NEW"開啟新的事務(wù)票罐,舉例來(lái)說,購(gòu)買東西是一個(gè)事務(wù)泞边,購(gòu)買每一本書又是一個(gè)事務(wù)该押,在購(gòu)買事務(wù)中調(diào)用購(gòu)買書,假如余額只有100塊阵谚,買兩本書蚕礼,一本是50烟具,一本是60。這樣的話默認(rèn)的就是在原來(lái)的事務(wù)奠蹬,錢不足是不會(huì)執(zhí)行成功朝聋,REQUIRED_NEW就會(huì)開啟新事務(wù),則會(huì)買一本書囤躁。
四冀痕、spring事務(wù)什么時(shí)候會(huì)失效
spring事務(wù)的原理是AOP,進(jìn)行了切面增強(qiáng)狸演,那么失效的根本原因是AOP不起作用了金度!
常見情況有如下幾種:
1、發(fā)生自調(diào)用严沥,類里面使用this調(diào)用本類的方法(this通常省略)猜极,此時(shí)這個(gè)this對(duì)象不是代理類,而是UserService對(duì)象本身消玄!解決方法很簡(jiǎn)單跟伏,讓那個(gè)this變成UserService的代理類即可!
2翩瓜、方法不是public的
@Transactional 只能用于 public 的方法上受扳,否則事務(wù)不會(huì)失效,如果要用在非 public 方法上兔跌,可 以開啟 AspectJ 代理模式勘高。
3、數(shù)據(jù)庫(kù)不支持事務(wù)
4坟桅、沒有被spring管理
5华望、異常被吃掉,事務(wù)不會(huì)回滾(或者拋出的異常沒有被定義仅乓,默認(rèn)為RuntimeException)