概念
- 為了保證對(duì)于數(shù)據(jù)庫(kù)的每一步操作都是可靠的,即使出現(xiàn)了異常情況神汹,也不至于破壞數(shù)據(jù)的完整性蜡塌。
事務(wù)可以在不同的Service中傳遞
- Spring的事務(wù)本質(zhì)是數(shù)據(jù)庫(kù)的事務(wù),事務(wù)的開啟是基于線程的溅蛉,同步類型方法調(diào)用過(guò)程中不會(huì)導(dǎo)致事務(wù)失效,但異步方法會(huì)失效他宛。
- 注解式開始事務(wù)用的是AOP船侧,故不能用于被同類方法調(diào)用的方法上。
四大特征厅各,或者說(shuō)保證
- 原子性
(Atomicity)
整個(gè)事務(wù)的所有操作勺爱,要么全部失敗,要么全部成功讯检,不可能停留在某個(gè)環(huán)節(jié)(受限于事務(wù)的超時(shí)時(shí)間)琐鲁,發(fā)生錯(cuò)誤時(shí)會(huì)回滾到一切操作執(zhí)行的狀態(tài) - 一致性
(Correspondence)
在事務(wù)開始前與結(jié)束后,數(shù)據(jù)庫(kù)的完整性約束不被破壞 - 隔離性
(Isolation)
隔離狀態(tài)執(zhí)行事務(wù)人灼,使它們好像是系統(tǒng)在給定時(shí)間內(nèi)執(zhí)行的唯一操作围段。如果有兩個(gè)事務(wù),運(yùn)行在相同的時(shí)間內(nèi)投放,執(zhí)行相同的功能奈泪,事務(wù)的隔離性將確保每一事務(wù)在系統(tǒng)中認(rèn)為只有該事務(wù)在使用系統(tǒng)。這種屬性有時(shí)稱為串行化灸芳,為了防止事務(wù)操作間的混淆涝桅,必須串行化或序列化請(qǐng) 求,使得在同一時(shí)間僅有一個(gè)請(qǐng)求用于同一數(shù)據(jù)烙样。 - Durability
(Durability)
在事務(wù)完成以后冯遂,該事務(wù)所對(duì)數(shù)據(jù)庫(kù)所作的更改便持久的保存在數(shù)據(jù)庫(kù)之中,并不會(huì)被回滾谒获。
五大隔離級(jí)別
TransactionDefinition.ISOLATION_DEFAULT:
默認(rèn)值蛤肌,表示使用底層數(shù)據(jù)庫(kù)的隔離級(jí)別。對(duì)于大多數(shù)數(shù)據(jù)庫(kù)而言批狱,通常此值就是TransactionDefinition.ISOLATION_READ_COMMITTED裸准。TransactionDefinition.ISOLATION_READ_UNCOMMITTED:
一個(gè)事務(wù)可以讀取另一個(gè)事務(wù)已經(jīng)修改但還未提交的數(shù)據(jù),不能防止臟讀與不可重復(fù)讀赔硫,因此極少使用TransactionDefinition.ISOLATION_READ_COMMITTED:
一個(gè)事務(wù)只可以讀取另一個(gè)事務(wù)已經(jīng)提交的數(shù)據(jù)炒俱,可以防止臟讀,為大多數(shù)情況下的推薦值。TransactionDefinition.ISOLATION_REPEATABLE_READ:
該隔離級(jí)別表示一個(gè)事務(wù)在整個(gè)過(guò)程中可以多次重復(fù)執(zhí)行某個(gè)查詢权悟,并且每次返回的記錄都相同恼蓬。即使在多次查詢之間有新增的數(shù)據(jù)滿足該查詢,這些新增的記錄也會(huì)被忽略僵芹。該級(jí)別可以防止臟讀和不可重復(fù)讀。TransactionDefinition.ISOLATION_SERIALIZABLE:
所有事務(wù)串行小槐,可以防止臟讀拇派、不可重復(fù)讀以及幻讀,但會(huì)嚴(yán)重影響效率不可重復(fù)讀和幻讀區(qū)別:不可重復(fù)讀的重點(diǎn)是修改凿跳;同樣的條件件豌,第1次和第2次讀取的值不一樣。 幻讀的重點(diǎn)在于新增或者刪除控嗜;同樣的條件茧彤, 第1次和第2次讀出來(lái)的記錄數(shù)不一樣。
七種傳播行為
傳播行為是指:如果在開始一個(gè)事務(wù)之前疆栏,已經(jīng)存在一個(gè)事務(wù)上下文曾掂,此時(shí)有若干選項(xiàng)可以指定一個(gè)事務(wù)性方法的執(zhí)行行為。在TransactionDefinition定義中包括了如下幾個(gè)表示傳播行為的常量:(下面以小紅和她男朋友去上班的故事簡(jiǎn)述)
參數(shù) | 說(shuō)明 |
---|---|
PROPAGATION_REQUIRED |
如果當(dāng)前存在事務(wù)壁顶,則加入該事務(wù)珠洗;如果當(dāng)前沒(méi)有事務(wù),則創(chuàng)建一個(gè)新的事務(wù)若专,這是spring默認(rèn)的傳播行為 |
PROPAGATION_REQUIRES_NEW |
創(chuàng)建一個(gè)新的事務(wù)许蓖,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起调衰。 |
PROPAGATION_SUPPORTS |
如果當(dāng)前存在事務(wù)膊爪,則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù)嚎莉,則以非事務(wù)的方式繼續(xù)運(yùn)行 |
PROPAGATION_NOT_SUPPORTED |
以非事務(wù)方式運(yùn)行米酬,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起 |
PROPAGATION_NEVER |
以非事務(wù)方式運(yùn)行趋箩,如果當(dāng)前存在事務(wù)淮逻,則拋出異常 |
PROPAGATION_MANDATORY |
如果當(dāng)前存在事務(wù),則加入該事務(wù)阁簸;如果當(dāng)前沒(méi)有事務(wù)爬早,則拋出異常 |
PROPAGATION_NESTED |
如果當(dāng)前存在事務(wù),則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來(lái)運(yùn)行启妹;如果當(dāng)前沒(méi)有事務(wù)筛严,則該取值等價(jià)于TransactionDefinition.PROPAGATION_REQUIRED |
TransactionDefinition.PROPAGATION_REQUIRED:
如果當(dāng)前存在事務(wù),則加入該事務(wù)饶米;如果當(dāng)前沒(méi)有事務(wù)桨啃,則創(chuàng)建一個(gè)新的事務(wù)车胡,這是spring默認(rèn)的傳播行為。
小紅和她男朋友關(guān)系很好照瘾,每天上班男朋友都會(huì)開車來(lái)接她匈棘,但也會(huì)有例外情況,所以小紅一般會(huì)乘坐男朋友車析命,如果男朋友沒(méi)能來(lái)就把自己的車開出來(lái)走TransactionDefinition.PROPAGATION_REQUIRES_NEW:
創(chuàng)建一個(gè)新的事務(wù)主卫,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起鹃愤。
小紅與男朋友鬧矛盾了簇搅,她決定接下來(lái)幾天都自己開車上班,即便男朋友來(lái)接她软吐,也要開自己車走瘩将,哼╭(╯^╰)╮。TransactionDefinition.PROPAGATION_SUPPORTS:
如果當(dāng)前存在事務(wù)凹耙,則加入該事務(wù)姿现;如果當(dāng)前沒(méi)有事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行肖抱。
小紅和男朋友很快和好如初了建钥,于是男朋友來(lái)接自己的時(shí)候,就會(huì)坐男朋友車一起走虐沥,可是有天小紅自己開車時(shí)車子輪胎壞掉了=-=熊经,只能送到4S店維修了=-=所以男朋友沒(méi)能來(lái)接自己的時(shí)候就只能步行去公司了=-=沒(méi)辦法,誰(shuí)讓自己住的地方偏僻連公交都沒(méi)有呢=-=TransactionDefinition.PROPAGATION_NOT_SUPPORTED:
以非事務(wù)方式運(yùn)行欲险,如果當(dāng)前存在事務(wù)镐依,則把當(dāng)前事務(wù)掛起。
小紅最近伙食有點(diǎn)好天试,著實(shí)有些發(fā)福了槐壳,看著日益豐滿的小肚子小紅咬咬牙決定減肥:每天餐飲減半、步行上班喜每,男朋友很是心疼务唐,每天上班時(shí)都會(huì)來(lái)她家接她,不忍心她步行上班带兜,可是她低估了一個(gè)姑娘的決心枫笛!哼,小紅是絕對(duì)不會(huì)像資本主義低頭的刚照,步行上班刑巧,節(jié)能環(huán)保!TransactionDefinition.PROPAGATION_NEVER:
以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù)啊楚,則拋出異常吠冤。
小紅的依然每天來(lái)接小紅,小紅害怕自己堅(jiān)定的意志會(huì)被動(dòng)搖恭理,畢竟暖暖的車廂確實(shí)比走路上班舒胡多了鴨拯辙,于是小紅告訴男票:再來(lái)動(dòng)搖自己的決心,小心我和你生氣Q占邸Q谋!(拋出異常)TransactionDefinition.PROPAGATION_MANDATORY:
如果當(dāng)前存在事務(wù),則加入該事務(wù)拍嵌;如果當(dāng)前沒(méi)有事務(wù),則拋出異常循诉。
小紅最近來(lái)了例假横辆,卻是不能再減肥了,畢竟傷身體的事情還是要杜絕的茄猫,可車子還在4S店沒(méi)有修好狈蚤,小紅就給男朋友下了硬性指標(biāo):必須來(lái)接我上班,否則我就向未來(lái)婆婆大人告狀划纽!╭(╯^╰)╮(拋出異常)TransactionDefinition.PROPAGATION_NESTED:
如果當(dāng)前存在事務(wù)脆侮,則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來(lái)運(yùn)行;如果當(dāng)前沒(méi)有事務(wù)勇劣,則該取值等價(jià)于TransactionDefinition.PROPAGATION_REQUIRED靖避。
小紅最近在網(wǎng)上買了一輛炒雞可耐的自行車,節(jié)能環(huán)保綠色出行的好伙伴吶比默,小紅愛(ài)不釋手于是每天上班都騎車幻捏,即便男朋友來(lái)接自己也要把自行車放到男票后備箱,待下班后回家路上騎(至于家庭轎車后備箱為什么這么大命咐。篡九。別問(wèn)我我也不知道~),男票不來(lái)就更好咯醋奠,自己騎車上班=-=嘟嘟嘟
這里需要指出的是榛臼,前面的六種事務(wù)傳播行為是 Spring 從 EJB 中引入的,他們共享相同的概念窜司。而 PROPAGATION_NESTED是 Spring 所特有的沛善。以 PROPAGATION_NESTED 啟動(dòng)的事務(wù)內(nèi)嵌于外部事務(wù)中(如果存在外部事務(wù)的話),此時(shí)塞祈,內(nèi)嵌事務(wù)并不是一個(gè)獨(dú)立的事務(wù)路呜,它依賴于外部事務(wù)的存在,只有通過(guò)外部的事務(wù)提交,才能引起內(nèi)部事務(wù)的提交胀葱,嵌套的子事務(wù)不能單獨(dú)提交漠秋。如果熟悉 JDBC 中的保存點(diǎn)(SavePoint)的概念,那嵌套事務(wù)就很容易理解了抵屿,其實(shí)嵌套的子事務(wù)就是保存點(diǎn)的一個(gè)應(yīng)用庆锦,一個(gè)事務(wù)中可以包括多個(gè)保存點(diǎn),每一個(gè)嵌套子事務(wù)轧葛。另外搂抒,外部事務(wù)的回滾也會(huì)導(dǎo)致嵌套子事務(wù)的回滾。
事務(wù)超時(shí)
所謂事務(wù)超時(shí)尿扯,就是指一個(gè)事務(wù)所允許執(zhí)行的最長(zhǎng)時(shí)間求晶,如果超過(guò)該時(shí)間限制但事務(wù)還沒(méi)有完成,則自動(dòng)回滾事務(wù)衷笋。在 TransactionDefinition 中以 int 的值來(lái)表示超時(shí)時(shí)間芳杏,其單位是秒。
事務(wù)的只讀屬性
事務(wù)的只讀屬性是指辟宗,對(duì)事務(wù)性資源進(jìn)行只讀操作或者是讀寫操作爵赵。所謂事務(wù)性資源就是指那些被事務(wù)管理的資源,比如數(shù)據(jù)源泊脐、 JMS 資源空幻,以及自定義的事務(wù)性資源等等。如果確定只對(duì)事務(wù)性資源進(jìn)行只讀操作容客,那么我們可以將事務(wù)標(biāo)志為只讀的秕铛,以提高事務(wù)處理的性能。在 TransactionDefinition 中以 boolean 類型來(lái)表示該事務(wù)是否只讀缩挑。
事務(wù)的回滾規(guī)則
通常情況下如捅,如果在事務(wù)中拋出了未檢查異常(繼承自 RuntimeException 的異常),則默認(rèn)將回滾事務(wù)调煎。如果沒(méi)有拋出任何異常镜遣,或者拋出了已檢查異常,則仍然提交事務(wù)士袄。這通常也是大多數(shù)開發(fā)者希望的處理方式悲关,也是 EJB 中的默認(rèn)處理方式。但是娄柳,我們可以根據(jù)需要人為控制事務(wù)在拋出某些未檢查異常時(shí)任然提交事務(wù)寓辱,或者在拋出某些已檢查異常時(shí)回滾事務(wù)。
編程式事務(wù)管理
首先配置PlatformTransactionManager的beans
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
其次在要使用事務(wù)的beans中注入PlatformTransactionManager
@Autowired
private PlatformTransactionManager transactionManager;
方法中使用
TransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
try {
// ... 執(zhí)行業(yè)務(wù)
// 執(zhí)行完后提交事務(wù)
transactionManager.commit(status);
} catch(Exception e) {
transactionManager.rollback(status);
}
很少使用了赤拒,這里就不在介紹了
聲明式事務(wù)管理
- 基于 <tx> 命名空間的聲明式事務(wù)管理
Spring 2.x 引入了 <tx> 命名空間秫筏,結(jié)合使用 <aop> 命名空間诱鞠,帶給開發(fā)人員配置聲明式事務(wù)的全新體驗(yàn),配置變得更加簡(jiǎn)單和靈活这敬。另外航夺,得益于 <aop> 命名空間的切點(diǎn)表達(dá)式支持,聲明式事務(wù)也變得更加強(qiáng)大崔涂。
<bean id="bankService"
class="footmark.spring.core.tx.declare.namespace.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
</bean>
<tx:advice id="bankAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transfer" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="bankPointcut" expression="execution(* *.transfer(..))"/>
<aop:advisor advice-ref="bankAdvice" pointcut-ref="bankPointcut"/>
</aop:config>
如果默認(rèn)的事務(wù)屬性可以滿足要求阳掐,可以使用簡(jiǎn)化版:
<bean id="bankService"
class="footmark.spring.core.tx.declare.namespace.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
</bean>
<tx:advice id="bankAdvice" transaction-manager="transactionManager">
<aop:config>
<aop:pointcut id="bankPointcut" expression="execution(**.transfer(..))"/>
<aop:advisor advice-ref="bankAdvice" pointcut-ref="bankPointcut"/>
</aop:config>
由于使用了切點(diǎn)表達(dá)式,我們就不需要針對(duì)每一個(gè)業(yè)務(wù)類創(chuàng)建一個(gè)代理對(duì)象了冷蚂。另外缭保,如果配置的事務(wù)管理器 Bean 的名字取值為“transactionManager”,則我們可以省略 <tx:advice> 的 transaction-manager 屬性蝙茶,因?yàn)樵搶傩缘哪J(rèn)值即為“transactionManager”艺骂。
- 基于 @Transactional 的聲明式事務(wù)管理
除了基于命名空間的事務(wù)配置方式,Spring 2.x 還引入了基于 Annotation 的方式隆夯,具體主要涉及@Transactional 標(biāo)注钳恕。@Transactional 可以作用于接口、接口方法吮廉、類以及類方法上苞尝。當(dāng)作用于類上時(shí)畸肆,該類的所有 public 方法將都具有該類型的事務(wù)屬性宦芦,同時(shí),我們也可以在方法級(jí)別使用該標(biāo)注來(lái)覆蓋類級(jí)別的定義轴脐。
@Transactional(propagation = Propagation.REQUIRED)
public boolean transfer(Long fromId调卑, Long toId, double amount) {
return bankDao.transfer(fromId大咱, toId恬涧, amount);
}
Spring 使用 BeanPostProcessor 來(lái)處理 Bean 中的標(biāo)注,因此我們需要在配置文件中作如下聲明來(lái)激活該后處理 Bean:
<tx:annotation-driven transaction-manager="transactionManager"/>
與前面相似碴巾,transaction-manager 屬性的默認(rèn)值是 transactionManager溯捆,如果事務(wù)管理器 Bean 的名字即為該值,則可以省略該屬性厦瓢。
雖然 @Transactional 注解可以作用于接口提揍、接口方法、類以及類方法上煮仇,但是 Spring 小組建議不要在接口或者接口方法上使用該注解劳跃,因?yàn)檫@只有在使用基于接口的代理時(shí)它才會(huì)生效。另外浙垫, @Transactional 注解應(yīng)該只被應(yīng)用到 public 方法上刨仑,這是由 Spring AOP 的本質(zhì)決定的郑诺。如果你在 protected、private 或者默認(rèn)可見(jiàn)性的方法上使用 @Transactional 注解杉武,這將被忽略辙诞,也不會(huì)拋出任何異常。
基于 <tx> 命名空間和基于 @Transactional 的事務(wù)聲明方式各有優(yōu)缺點(diǎn)艺智√纫基于 <tx> 的方式,其優(yōu)點(diǎn)是與切點(diǎn)表達(dá)式結(jié)合十拣,功能強(qiáng)大封拧。利用切點(diǎn)表達(dá)式,一個(gè)配置可以匹配多個(gè)方法夭问,而基于 @Transactional 的方式必須在每一個(gè)需要使用事務(wù)的方法或者類上用 @Transactional 標(biāo)注泽西,盡管可能大多數(shù)事務(wù)的規(guī)則是一致的,但是對(duì) @Transactional 而言缰趋,也無(wú)法重用捧杉,必須逐個(gè)指定。另一方面秘血,基于 @Transactional 的方式使用起來(lái)非常簡(jiǎn)單明了味抖,沒(méi)有學(xué)習(xí)成本。開發(fā)人員可以根據(jù)需要灰粮,任選其中一種使用仔涩,甚至也可以根據(jù)需要混合使用這兩種方式。
- 聲明式事務(wù)的問(wèn)題
- 同一個(gè)類的方法相互調(diào)用粘舟,會(huì)使用同一個(gè)事務(wù)熔脂,事務(wù)的傳播行為在同類方法間的調(diào)用失效
- 同一個(gè)類的方法相互調(diào)用時(shí),假設(shè)A調(diào)用B柑肴,若A沒(méi)有事務(wù)霞揉,則B不可能擁有事務(wù)
后記
PROPAGATION_NESTED
嵌套事務(wù)與PROPAGATION_REQUIRES_NEW
事務(wù)區(qū)別
- 假設(shè)有方法A與方法B, A存在外部事務(wù)晰骑,B存在內(nèi)部事務(wù)适秩,A方法的事務(wù)包含了調(diào)用B方法
public void A() {
// 外部事務(wù)
B();
}
public void B() {
// 內(nèi)部事務(wù)
}
- B方法的內(nèi)部事務(wù)分別使用
PROPAGATION_REQUIRES_NEW
與PROPAGATION_NESTED
- 這里根據(jù)兩個(gè)事務(wù)的不同行為進(jìn)行比較
-
new_result_B
表示使用B方法使用PROPAGATION_REQUIRES_NEW
時(shí),B的結(jié)果 -
new_result_A
表示使用B方法使用PROPAGATION_REQUIRES_NEW
時(shí)硕舆,A的結(jié)果 -
nested_result_B
表示使用B方法使用PROPAGATION_NESTED
時(shí)秽荞,B的結(jié)果 -
nested_result_A
表示使用B方法使用PROPAGATION_NESTED
時(shí),A的結(jié)果
B行為 | A行為 | new_result_B | new_result_A | nested_result_B | nested_result_A |
---|---|---|---|---|---|
提交 | 提交 | 成功 | 成功 | 成功 | 成功 |
提交 | 回滾 | 成功 | 失敗 | 失敗 | 失敗 |
回滾 | 提交 | 失敗 | 成功 | 失敗 | 成功 |
回滾 | 回滾 | 失敗 | 失敗 | 失敗 | 失敗 |
- 總結(jié):內(nèi)部嵌套事務(wù)岗宣,擁有自己的回滾權(quán)蚂会,但沒(méi)有提交權(quán)。
但new事務(wù)耗式,既有自己的回滾權(quán)胁住,同時(shí)具有提交權(quán)趁猴。
但相同的是,他們都不會(huì)對(duì)外部事務(wù)造成影響