事務(wù):
事務(wù)是邏輯上的一組操作,要么都執(zhí)行丸卷,要么都不執(zhí)行盅视。
事物的特性:(ACID)
原子性: 事務(wù)是最小的執(zhí)行單位,不允許分割旦万。事務(wù)的原子性確保動作要么全部完成闹击,要么完全不起作用;
一致性: 執(zhí)行事務(wù)前后成艘,數(shù)據(jù)保持一致赏半;
隔離性: 并發(fā)訪問數(shù)據(jù)庫時,一個用戶的事物不被其他事物所干擾淆两,各并發(fā)事務(wù)之間數(shù)據(jù)庫是獨(dú)立的断箫;
持久性: 一個事務(wù)被提交之后。它對數(shù)據(jù)庫中數(shù)據(jù)的改變是持久的秋冰,即使數(shù)據(jù)庫發(fā)生故障也不應(yīng)該對其有任何影響仲义。
Spring事務(wù)管理接口:
- PlatformTransactionManager: (平臺)事務(wù)管理器
- TransactionDefinition: 事務(wù)定義信息(事務(wù)隔離級別、傳播行為剑勾、超時埃撵、只讀、回滾規(guī)則)
- TransactionStatus: 事務(wù)運(yùn)行狀態(tài)
org.springframework.transaction.PlatformTransactionManager 接口
Spring并不直接管理事務(wù)虽另,而是提供了多種事務(wù)管理器 暂刘,他們將事務(wù)管理的職責(zé)委托給Hibernate或者JTA等持久化機(jī)制所提供的相關(guān)平臺框架的事務(wù)來實(shí)現(xiàn)。
通過這個接口捂刺,Spring為各個平臺如JDBC谣拣、Hibernate等都提供了對應(yīng)的事務(wù)管理器募寨,但是具體的實(shí)現(xiàn)就是各個平臺自己的事情了。
PlatformTransactionManager接口中定義了三個方法:
創(chuàng)建森缠、提交拔鹰、回滾
Public interface PlatformTransactionManager()...{
// Return a currently active transaction or create a new one, according to the specified propagation behavior(根據(jù)指定的傳播行為,返回當(dāng)前活動的事務(wù)或創(chuàng)建一個新事務(wù)辅鲸。)
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
// Commit the given transaction, with regard to its status(使用事務(wù)目前的狀態(tài)提交事務(wù))
Void commit(TransactionStatus status) throws TransactionException;
// Perform a rollback of the given transaction(對執(zhí)行的事務(wù)進(jìn)行回滾)
Void rollback(TransactionStatus status) throws TransactionException;
}
PlatformTransactionManager根據(jù)不同持久層框架所對應(yīng)的接口實(shí)現(xiàn)類:
比如我們在使用JDBC或者iBatis(就是Mybatis)進(jìn)行數(shù)據(jù)持久化操作時,我們的xml配置通常如下:
<!-- 事務(wù)管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 數(shù)據(jù)源 -->
<property name="dataSource" ref="dataSource" />
</bean>
TransactionDefinition接口
上面介紹的PlatformTransactionManager接口定義了getTransaction(TransactionDefinition definition) 方法來得到一個事務(wù)格郁,這個方法里面的參數(shù)就是 TransactionDefinition類 ,這個類就定義了一些基本的事務(wù)屬性独悴。
事務(wù)屬性
事務(wù)屬性可以理解成事務(wù)的一些基本配置例书,描述了事務(wù)策略如何應(yīng)用到方法上。
事務(wù)屬性包含了5個方面:
ransactionDefinition接口中的方法如下:
TransactionDefinition接口中定義了5個方法以及一些表示事務(wù)屬性的常量比如隔離級別刻炒、傳播行為等等的常量决采。
我下面只是列出了TransactionDefinition接口中的方法而沒有給出接口中定義的常量,該接口中的常量信息會在后面依次介紹到坟奥。
public interface TransactionDefinition {
// 返回事務(wù)的傳播行為
int getPropagationBehavior();
// 返回事務(wù)的隔離級別树瞭,事務(wù)管理器根據(jù)它來控制另外一個事務(wù)可以看到本事務(wù)內(nèi)的哪些數(shù)據(jù)
int getIsolationLevel();
//返回事務(wù)的名字
String getName();
// 返回事務(wù)必須在多少秒內(nèi)完成
int getTimeout();
// 返回是否優(yōu)化為只讀事務(wù)爱谁。
boolean isReadOnly();
}
(1)事務(wù)隔離級別(定義了一個事務(wù)可能受其他并發(fā)事務(wù)影響的程度):
我們先來看一下 并發(fā)事務(wù)帶來的問題 晒喷,然后再來介紹一下 TransactionDefinition 接口 中定義了五個表示隔離級別的常量。
并發(fā)事務(wù)帶來的問題:
在典型的應(yīng)用程序中访敌,多個事務(wù)并發(fā)運(yùn)行凉敲,經(jīng)常會操作相同的數(shù)據(jù)來完成各自的任務(wù)(多個用戶對統(tǒng)一數(shù)據(jù)進(jìn)行操作)。并發(fā)雖然是必須的寺旺,但可能會導(dǎo)致以下的問題:
- 臟讀(Dirty read): 當(dāng)一個事務(wù)正在訪問數(shù)據(jù)并且對數(shù)據(jù)進(jìn)行了修改爷抓,而這種修改還沒有提交到數(shù)據(jù)庫中,這時另外一個事務(wù)也訪問了這個數(shù)據(jù)阻塑,然后使用了這個數(shù)據(jù)蓝撇。因?yàn)檫@個數(shù)據(jù)是還沒有提交的數(shù)據(jù),那么另外一個事務(wù)讀到的這個數(shù)據(jù)是“臟數(shù)據(jù)”陈莽,依據(jù)“臟數(shù)據(jù)”所做的操作可能是不正確的渤昌。
- 丟失修改(Lost to modify): 指在一個事務(wù)讀取一個數(shù)據(jù)時,另外一個事務(wù)也訪問了該數(shù)據(jù)传透,那么在第一個事務(wù)中修改了這個數(shù)據(jù)后耘沼,第二個事務(wù)也修改了這個數(shù)據(jù)。這樣第一個事務(wù)內(nèi)的修改結(jié)果就被丟失朱盐,因此稱為丟失修改群嗤。
例如:事務(wù)1讀取某表中的數(shù)據(jù)A=20,事務(wù)2也讀取A=20兵琳,事務(wù)1修改A=A-1狂秘,事務(wù)2也修改A=A-1骇径,最終結(jié)果A=19,事務(wù)1的修改被丟失者春。
- 不可重復(fù)讀(Unrepeatableread): 指在一個事務(wù)內(nèi)多次讀同一數(shù)據(jù)破衔。在這個事務(wù)還沒有結(jié)束時,另一個事務(wù)也訪問該數(shù)據(jù)钱烟。那么晰筛,在第一個事務(wù)中的兩次讀數(shù)據(jù)之間,由于第二個事務(wù)的修改導(dǎo)致第一個事務(wù)兩次讀取的數(shù)據(jù)可能不太一樣拴袭。這就發(fā)生了在一個事務(wù)內(nèi)兩次讀到的數(shù)據(jù)是不一樣的情況读第,因此稱為不可重復(fù)讀。
- 幻讀(Phantom read): 幻讀與不可重復(fù)讀類似拥刻。它發(fā)生在一個事務(wù)(T1)讀取了幾行數(shù)據(jù)怜瞒,接著另一個并發(fā)事務(wù)(T2)插入了一些數(shù)據(jù)時。在隨后的查詢中般哼,第一個事務(wù)(T1)就會發(fā)現(xiàn)多了一些原本不存在的記錄吴汪,就好像發(fā)生了幻覺一樣,所以稱為幻讀蒸眠。
不可重復(fù)度和幻讀區(qū)別:
不可重復(fù)讀的重點(diǎn)是修改漾橙,幻讀的重點(diǎn)在于新增或者刪除。
例1(同樣的條件, 你讀取過的數(shù)據(jù), 再次讀取出來發(fā)現(xiàn)值不一樣了 ):事務(wù)1中的A先生讀取自己的工資為1000的操作還沒完成楞卡,事務(wù)2中的B先生就修改了A的工資為2000近刘,導(dǎo)致A再讀自己的工資時工資變?yōu)?000;這就是不可重復(fù)讀臀晃。
例2(同樣的條件, 第1次和第2次讀出來的記錄數(shù)不一樣 ):假某工資單表中工資大于3000的有4人,事務(wù)1讀取了所有工資大于3000的人介劫,共查到4條記錄徽惋,這時事務(wù)2 又插入了一條工資大于3000的記錄,事務(wù)1再次讀取時查到的記錄就變?yōu)榱?條座韵,這樣就導(dǎo)致了幻讀险绘。
隔離級別
TransactionDefinition 接口中定義了五個表示隔離級別的常量:
TransactionDefinition.ISOLATION_DEFAULT: 使用后端數(shù)據(jù)庫默認(rèn)的隔離級別,Mysql 默認(rèn)采用的 REPEATABLE_READ隔離級別 Oracle 默認(rèn)采用的 READ_COMMITTED隔離級別.
TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔離級別誉碴,允許讀取尚未提交的數(shù)據(jù)變更宦棺,可能會導(dǎo)致臟讀、幻讀或不可重復(fù)讀
TransactionDefinition.ISOLATION_READ_COMMITTED: 允許讀取并發(fā)事務(wù)已經(jīng)提交的數(shù)據(jù)黔帕,可以阻止臟讀代咸,但是幻讀或不可重復(fù)讀仍有可能發(fā)生
TransactionDefinition.ISOLATION_REPEATABLE_READ: 對同一字段的多次讀取結(jié)果都是一致的,除非數(shù)據(jù)是被本身事務(wù)自己所修改成黄,可以阻止臟讀和不可重復(fù)讀呐芥,但幻讀仍有可能發(fā)生逻杖。
TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔離級別,完全服從ACID的隔離級別思瘟。所有的事務(wù)依次逐個執(zhí)行荸百,這樣事務(wù)之間就完全不可能產(chǎn)生干擾,也就是說滨攻,該級別可以防止臟讀够话、不可重復(fù)讀以及幻讀。但是這將嚴(yán)重影響程序的性能光绕。通常情況下也不會用到該級別女嘲。
(2)事務(wù)傳播行為(為了解決業(yè)務(wù)層方法之間互相調(diào)用的事務(wù)問題):
當(dāng)事務(wù)方法被另一個事務(wù)方法調(diào)用時,必須指定事務(wù)應(yīng)該如何傳播奇钞。例如:方法可能繼續(xù)在現(xiàn)有事務(wù)中運(yùn)行澡为,也可能開啟一個新事務(wù),并在自己的事務(wù)中運(yùn)行景埃。在TransactionDefinition定義中包括了如下幾個表示傳播行為的常量:
支持當(dāng)前事務(wù)的情況:
TransactionDefinition.PROPAGATION_REQUIRED: 如果當(dāng)前存在事務(wù)媒至,則加入該事務(wù);如果當(dāng)前沒有事務(wù)谷徙,則創(chuàng)建一個新的事務(wù)拒啰。
TransactionDefinition.PROPAGATION_SUPPORTS: 如果當(dāng)前存在事務(wù),則加入該事務(wù)完慧;如果當(dāng)前沒有事務(wù)谋旦,則以非事務(wù)的方式繼續(xù)運(yùn)行。
TransactionDefinition.PROPAGATION_MANDATORY: 如果當(dāng)前存在事務(wù)屈尼,則加入該事務(wù)册着;如果當(dāng)前沒有事務(wù),則拋出異常脾歧。
不支持當(dāng)前事務(wù)的情況:
TransactionDefinition.PROPAGATION_REQUIRES_NEW: 創(chuàng)建一個新的事務(wù)甲捏,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起鞭执。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事務(wù)方式運(yùn)行司顿,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起兄纺。
TransactionDefinition.PROPAGATION_NEVER: 以非事務(wù)方式運(yùn)行大溜,如果當(dāng)前存在事務(wù),則拋出異常估脆。
其他情況:
- TransactionDefinition.PROPAGATION_NESTED: 如果當(dāng)前存在事務(wù)钦奋,則創(chuàng)建一個事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運(yùn)行;如果當(dāng)前沒有事務(wù),則該取值等價于TransactionDefinition.PROPAGATION_REQUIRED(創(chuàng)建一個新的事務(wù))锨苏。
這里需要指出的是疙教,前面的六種事務(wù)傳播行為是 Spring 從 EJB 中引入的,他們共享相同的概念伞租。而 PROPAGATION_NESTED 是 Spring 所特有的贞谓。以 PROPAGATION_NESTED 啟動的事務(wù)內(nèi)嵌于外部事務(wù)中(如果存在外部事務(wù)的話),此時葵诈,內(nèi)嵌事務(wù)并不是一個獨(dú)立的事務(wù)裸弦,它依賴于外部事務(wù)的存在,只有通過外部的事務(wù)提交作喘,才能引起內(nèi)部事務(wù)的提交理疙,嵌套的子事務(wù)不能單獨(dú)提交。如果熟悉 JDBC 中的保存點(diǎn)(SavePoint)的概念泞坦,那嵌套事務(wù)就很容易理解了窖贤,其實(shí)嵌套的子事務(wù)就是保存點(diǎn)的一個應(yīng)用,一個事務(wù)中可以包括多個保存點(diǎn)贰锁,每一個嵌套子事務(wù)赃梧。另外,外部事務(wù)的回滾也會導(dǎo)致嵌套子事務(wù)的回滾豌熄。
(3) 事務(wù)超時屬性(一個事務(wù)允許執(zhí)行的最長時間)
所謂事務(wù)超時授嘀,就是指一個事務(wù)所允許執(zhí)行的最長時間,如果超過該時間限制但事務(wù)還沒有完成锣险,則自動回滾事務(wù)蹄皱。在 TransactionDefinition 中以 int 的值來表示超時時間,其單位是秒芯肤。
(4) 事務(wù)只讀屬性(對事物資源是否執(zhí)行只讀操作)
事務(wù)的只讀屬性是指巷折,對事務(wù)性資源進(jìn)行只讀操作或者是讀寫操作。所謂事務(wù)性資源就是指那些被事務(wù)管理的資源崖咨,比如數(shù)據(jù)源盔几、 JMS 資源,以及自定義的事務(wù)性資源等等掩幢。如果確定只對事務(wù)性資源進(jìn)行只讀操作,那么我們可以將事務(wù)標(biāo)志為只讀的上鞠,以提高事務(wù)處理的性能际邻。在 TransactionDefinition 中以 boolean 類型來表示該事務(wù)是否只讀卒茬。
(5) 回滾規(guī)則(定義事務(wù)回滾規(guī)則)
這些規(guī)則定義了哪些異常會導(dǎo)致事務(wù)回滾而哪些不會沿癞。默認(rèn)情況下,事務(wù)只有遇到運(yùn)行期異常時才會回滾霹抛,而在遇到檢查型異常時不會回滾(這一行為與EJB的回滾行為是一致的)。
但是你可以聲明事務(wù)在遇到特定的檢查型異常時像遇到運(yùn)行期異常那樣回滾轮听。同樣骗露,你還可以聲明事務(wù)遇到特定的異常不回滾,即使這些異常是運(yùn)行期異常血巍。
TransactionStatus接口
TransactionStatus接口用來記錄事務(wù)的狀態(tài) 該接口定義了一組方法,用來獲取或判斷事務(wù)的相應(yīng)狀態(tài)信息萧锉。
PlatformTransactionManager.getTransaction(…) 方法返回一個 TransactionStatus 對象。返回的TransactionStatus 對象可能代表一個新的或已經(jīng)存在的事務(wù)(如果在當(dāng)前調(diào)用堆棧有一個符合條件的事務(wù))述寡。
TransactionStatus接口接口內(nèi)容如下:
public interface TransactionStatus{
boolean isNewTransaction(); // 是否是新的事物
boolean hasSavepoint(); // 是否有恢復(fù)點(diǎn)
void setRollbackOnly(); // 設(shè)置為只回滾
boolean isRollbackOnly(); // 是否為只回滾
boolean isCompleted; // 是否已完成
}
Spring支持兩種方式的事務(wù)管理:
編程式事務(wù)管理: 通過Transaction Template手動管理事務(wù)柿隙,實(shí)際應(yīng)用中很少使用
使用XML配置聲明式事務(wù): 推薦使用(代碼侵入性最小)鲫凶,實(shí)際是通過AOP實(shí)現(xiàn)
實(shí)現(xiàn)聲明式事務(wù)的四種方式:
- 基于 TransactionInterceptor 的聲明式事務(wù): Spring 聲明式事務(wù)的基礎(chǔ)禀崖,通常也不建議使用這種方式,但是與前面一樣螟炫,了解這種方式對理解 Spring 聲明式事務(wù)有很大作用波附。
- 基于 TransactionProxyFactoryBean 的聲明式事務(wù): 第一種方式的改進(jìn)版本,簡化的配置文件的書寫昼钻,這是 Spring 早期推薦的聲明式事務(wù)管理方式掸屡,但是在 Spring 2.0 中已經(jīng)不推薦了。
- 基于< tx> 和< aop>命名空間的聲明式事務(wù)管理: 目前推薦的方式换吧,其最大特點(diǎn)是與 Spring AOP 結(jié)合緊密折晦,可以充分利用切點(diǎn)表達(dá)式的強(qiáng)大支持,使得管理事務(wù)更加靈活沾瓦。
- 基于 @Transactional 的全注解方式: 將聲明式事務(wù)管理簡化到了極-致满着。開發(fā)人員只需在配置文件中加上一行啟用相關(guān)后處理 Bean 的配置,然后在需要實(shí)施事務(wù)管理的方法或者類上使用 @Transactional 指定事務(wù)規(guī)則即可實(shí)現(xiàn)事務(wù)管理贯莺,而且功能也不必其他方式遜色风喇。
出自作者:Snailclimb
原文鏈接:https://juejin.im/post/5b00c52ef265da0b95276091