由淺入深談?wù)搒pring事務(wù)

很多人喜歡這篇文章,特此同步過來 由淺入深談?wù)搒pring事務(wù)

前言

這篇其實也要歸納到《常識》系列中,但這重點又是spring的介紹,故歸檔在spring系列中。

工作很多年藏鹊,除了學生時代學過,事務(wù)還真沒有用過转锈。過去開發(fā)游戲時伙判,完全不用事務(wù);現(xiàn)在互聯(lián)網(wǎng)開發(fā)黑忱,也沒有使用事務(wù)的場景宴抚,不要見怪。

概念

對于事務(wù)(Transaction)的概念甫煞,網(wǎng)上有各種版本菇曲,大同小異,

事務(wù)就是是由一系列對系統(tǒng)中數(shù)據(jù)進行讀寫的操作組成的一個程序執(zhí)行單元抚吠,狹義上的事務(wù)特指數(shù)據(jù)庫事務(wù)常潮。

事務(wù)是一系列的動作,它們綜合在一起才是一個完整的工作單元楷力,這些動作必須全部完成喊式,如果有一個失敗的話孵户,那么事務(wù)就會回滾到最開始的狀態(tài),仿佛什么都沒發(fā)生過一樣岔留。

在企業(yè)級應(yīng)用程序開發(fā)中夏哭,事務(wù)管理必不可少的技術(shù),用來確保數(shù)據(jù)的完整性和一致性献联。

比如你去ATM機取1000塊錢竖配,大體有兩個步驟:首先輸入密碼金額,銀行卡扣掉1000元錢里逆;然后ATM出1000元錢进胯。這兩個步驟必須是要么都執(zhí)行要么都不執(zhí)行。如果銀行卡扣除了1000塊但是ATM出錢失敗的話原押,你將會損失1000元胁镐;如果銀行卡扣錢失敗但是ATM卻出了1000塊,那么銀行將損失1000元诸衔。所以盯漂,如果一個步驟成功另一個步驟失敗對雙方都不是好事,如果不管哪一個步驟失敗了以后署隘,整個取錢過程都能回滾宠能,也就是完全取消所有操作的話亚隙,這對雙方都是極好的磁餐。

事務(wù)的特性

大名鼎鼎的ACID

  1. 原子性(Atomicity),事務(wù)必須是一個原子的操作序列單元阿弃,一次事務(wù)只允許存在兩種狀態(tài)诊霹,全部成功或全部失敗,任何一個操作失敗都將導致整個事務(wù)失敗
  2. 一致性(Consistency)渣淳,事務(wù)的執(zhí)行不能破壞系統(tǒng)數(shù)據(jù)的完整性和一致性脾还,如果未完成的事務(wù)對系統(tǒng)數(shù)據(jù)的修改有一部分已經(jīng)寫入物理數(shù)據(jù)庫,這時系統(tǒng)數(shù)據(jù)就處于不一致狀態(tài)
  3. 隔離性(Isolation)入愧,在并發(fā)環(huán)境中鄙漏,不同的事務(wù)操作相同的數(shù)據(jù)時,虛相互隔離不能相互干擾
  4. 持久性(Durability)棺蛛,事務(wù)一旦提交怔蚌,對系統(tǒng)數(shù)據(jù)的變更就應(yīng)該是永久的,必須被永久保存下來旁赊,即使服務(wù)器宕機了桦踊,只要數(shù)據(jù)庫能夠重新啟動,就一定能夠恢復到事務(wù)成功結(jié)束時的狀態(tài)

事務(wù)并發(fā)處理問題

如果沒有鎖定且多個用戶同時訪問一個數(shù)據(jù)庫终畅,則當他們的事務(wù)同時使用相同的數(shù)據(jù)時可能會發(fā)生問題籍胯。由于并發(fā)操作帶來的數(shù)據(jù)不一致性包括:丟失數(shù)據(jù)修改竟闪、讀”臟”數(shù)據(jù)(臟讀)、不可重復讀杖狼、產(chǎn)生幽靈數(shù)據(jù):

假設(shè)數(shù)據(jù)庫中有如下一張表:

image

第一類丟失更新(lost update)

回滾丟失

在完全未隔離事務(wù)的情況下炼蛤,兩個事物更新同一條數(shù)據(jù)資源,某一事物異常終止本刽,回滾造成第一個完成的更新也同時丟失鲸湃。


image

在T1時刻開啟了事務(wù)1,T2時刻開啟了事務(wù)2子寓,

在T3時刻事務(wù)1從數(shù)據(jù)庫中取出了id="402881e535194b8f0135194b91310001"的數(shù)據(jù)暗挑,

T4時刻事務(wù)2取出了同一條數(shù)據(jù),

T5時刻事務(wù)1將age字段值更新為30斜友,

T6時刻事務(wù)2更新age為35并提交了數(shù)據(jù)炸裆,

但是T7事務(wù)1回滾了事務(wù)age最后的值依然為20,事務(wù)2的更新丟失了鲜屏,

這種情況就叫做"第一類丟失更新(lost update)"烹看。

臟讀(dirty read)

事務(wù)沒提交,提前讀取

如果第二個事務(wù)查詢到第一個事務(wù)還未提交的更新數(shù)據(jù)洛史,形成臟讀


image

在T1時刻開啟了事務(wù)1惯殊,T2時刻開啟了事務(wù)2,

在T3時刻事務(wù)1從數(shù)據(jù)庫中取出了id="402881e535194b8f0135194b91310001"的數(shù)據(jù)也殖,

在T5時刻事務(wù)1將age的值更新為30土思,但是事務(wù)還未提交,

T6時刻事務(wù)2讀取同一條記錄忆嗜,獲得age的值為30己儒,但是事務(wù)1還未提交,

若在T7時刻事務(wù)1回滾了事務(wù)2的數(shù)據(jù)就是錯誤的數(shù)據(jù)(臟數(shù)據(jù))捆毫,

這種情況叫做" 臟讀(dirty read)"闪湾。

虛讀(phantom read)

一個事務(wù)執(zhí)行兩次查詢,第二次結(jié)果集包含第一次中沒有或者某些行已被刪除绩卤,造成兩次結(jié)果不一致途样,只是另一個事務(wù)在這兩次查詢中間插入或者刪除了數(shù)據(jù)造成的

image

在T1時刻開啟了事務(wù)1,T2時刻開啟了事務(wù)2濒憋,

T3時刻事務(wù)1從數(shù)據(jù)庫中查詢所有記錄何暇,記錄總共有一條,

T4時刻事務(wù)2向數(shù)據(jù)庫中插入一條記錄跋炕,T6時刻事務(wù)2提交事務(wù)赖晶。

T7事務(wù)1再次查詢數(shù)據(jù)數(shù)據(jù)時,記錄變成兩條了。

這種情況是"虛讀(phantom read)"遏插。

不可重復讀(unrepeated read)

一個事務(wù)兩次讀取同一行數(shù)據(jù)捂贿,結(jié)果得到不同狀態(tài)結(jié)果,如中間正好另一個事務(wù)更新了該數(shù)據(jù)胳嘲,兩次結(jié)果相異厂僧,不可信任


image

在T1時刻開啟了事務(wù)1,T2時刻開啟了事務(wù)2了牛,

在T3時刻事務(wù)1從數(shù)據(jù)庫中取出了id="402881e535194b8f0135194b91310001"的數(shù)據(jù)颜屠,此時age=20,

T4時刻事務(wù)2查詢同一條數(shù)據(jù)鹰祸,

T5事務(wù)2更新數(shù)據(jù)age=30甫窟,T6時刻事務(wù)2提交事務(wù),

T7事務(wù)1查詢同一條數(shù)據(jù)蛙婴,發(fā)現(xiàn)數(shù)據(jù)與第一次不一致粗井。

這種情況就是"不可重復讀(unrepeated read)"

第二類丟失更新(second lost updates)

覆蓋丟失

不可重復讀的特殊情況,如果兩個事務(wù)都讀取同一行街图,然后兩個都進行寫操作浇衬,并提交,第一個事務(wù)所做的改變就會丟失餐济。

image

在T1時刻開啟了事務(wù)1耘擂,T2時刻開啟了事務(wù)2,

T3時刻事務(wù)1更新數(shù)據(jù)age=25絮姆,

T5時刻事務(wù)2更新數(shù)據(jù)age=30醉冤,

T6時刻提交事務(wù),

T7時刻事務(wù)2提交事務(wù)滚朵,把事務(wù)1的更新覆蓋了冤灾。

這種情況就是"第二類丟失更新(second lost updates)"前域。

并發(fā)問題總結(jié)

不可重復讀的重點是修改 :

同樣的條件 , 你讀取過的數(shù)據(jù) , 再次讀取出來發(fā)現(xiàn)值不一樣了

幻讀的重點在于新增或者刪除

同樣的條件 , 第 1 次和第 2 次讀出來的記錄數(shù)不一樣

第一類更新丟失(回滾丟失)

第二類更新丟失(覆蓋丟失)

隔離級別

解決并發(fā)問題的途徑是什么?答案是:采取有效的隔離機制辕近。怎樣實現(xiàn)事務(wù)的隔離呢?隔離機制的實現(xiàn)必須使用鎖

一般在編程的時候只需要設(shè)置隔離等級

數(shù)據(jù)庫系統(tǒng)提供四種事務(wù)隔離級別:

  1. 未提交讀(READ UNCOMMITTED )

最低隔離級別匿垄,一個事務(wù)能讀取到別的事務(wù)未提交的更新數(shù)據(jù)移宅,很不安全,可能出現(xiàn)丟失更新椿疗、臟讀漏峰、不可重復讀、幻讀届榄;

  1. 提交讀(READ COMMITTED)

一個事務(wù)能讀取到別的事務(wù)提交的更新數(shù)據(jù)浅乔,不能看到未提交的更新數(shù)據(jù),不會出現(xiàn)丟失更新、臟讀靖苇,但可能出現(xiàn)不可重復讀席噩、幻讀;

  1. 可重復讀(REPEATABLE READ)

保證同一事務(wù)中先后執(zhí)行的多次查詢將返回同一結(jié)果贤壁,不受其他事務(wù)影響悼枢,不可能出現(xiàn)丟失更新、臟讀脾拆、不可重復讀馒索,但可能出現(xiàn)幻讀;

  1. 序列化(SERIALIZABLE)

最高隔離級別名船,不允許事務(wù)并發(fā)執(zhí)行绰上,而必須串行化執(zhí)行,最安全渠驼,不可能出現(xiàn)更新渔期、臟讀、不可重復讀渴邦、幻讀疯趟,但是效率最低。

image

隔離級別越高谋梭,數(shù)據(jù)庫事務(wù)并發(fā)執(zhí)行性能越差信峻,能處理的操作越少。
所以一般地瓮床,推薦使用REPEATABLE READ級別保證數(shù)據(jù)的讀一致性盹舞。
對于幻讀的問題,可以通過加鎖來防止

image

MySQL支持這四種事務(wù)等級隘庄,默認事務(wù)隔離級別是REPEATABLE READ踢步。

Oracle數(shù)據(jù)庫支持READ COMMITTED 和 SERIALIZABLE這兩種事務(wù)隔離級別,
所以O(shè)racle數(shù)據(jù)庫不支持臟讀

Oracle數(shù)據(jù)庫默認的事務(wù)隔離級別是READ COMMITTED

不可重復讀和幻讀的區(qū)別是丑掺,不可重復讀對應(yīng)的表的操作是更改(UPDATE)获印,而幻讀對應(yīng)的表的操作是插入(INSERT),兩種的應(yīng)對策略不一樣街州。對于不可重復讀兼丰,只需要采用行級鎖防止該記錄被更新即可,而對于幻讀必須加個表級鎖唆缴,防止在表中插入數(shù)據(jù)

樂觀鎖(Optimistic Lock)和悲觀鎖(Pessimistic Lock)

最重要的分類就是樂觀鎖(Optimistic Lock)和悲觀鎖(Pessimistic Lock)鳍征,這實際上是兩種鎖策略

樂觀鎖,顧名思義就是非常樂觀面徽,非常相信真善美艳丛,每次去讀數(shù)據(jù)都認為其它事務(wù)沒有在寫數(shù)據(jù),所以就不上鎖,快樂的讀取數(shù)據(jù)氮双,而只在提交數(shù)據(jù)的時候判斷其它事務(wù)是否搞過這個數(shù)據(jù)了旺聚,如果搞過就rollback。樂觀鎖相當于一種檢測沖突的手段眶蕉,可通過為記錄添加版本或添加時間戳來實現(xiàn)砰粹。

悲觀鎖,對其它事務(wù)抱有保守的態(tài)度造挽,每次去讀數(shù)據(jù)都認為其它事務(wù)想要作祟碱璃,所以每次讀數(shù)據(jù)的時候都會上鎖,直到取出數(shù)據(jù)饭入。悲觀鎖大多數(shù)情況下依靠數(shù)據(jù)庫的鎖機制實現(xiàn)嵌器,以保證操作最大程度的獨占性,但隨之而來的是各種開銷谐丢。悲觀鎖相當于一種避免沖突的手段爽航。

選擇標準:如果并發(fā)量不大,或數(shù)據(jù)沖突的后果不嚴重乾忱,則可以使用樂觀鎖讥珍;而如果并發(fā)量大或數(shù)據(jù)沖突后果比較嚴重(對用戶不友好),那么就使用悲觀鎖窄瘟。

分共享鎖(S鎖衷佃,Shared Lock)和排他鎖(X鎖,Exclusive Lock)

從讀寫角度蹄葱,分共享鎖(S鎖氏义,Shared Lock)和排他鎖(X鎖,Exclusive Lock)图云,也叫讀鎖(Read Lock)和寫鎖(Write Lock)惯悠。
理解:

持有S鎖的事務(wù)只讀不可寫。

如果事務(wù)A對數(shù)據(jù)D加上S鎖后竣况,其它事務(wù)只能對D加上S鎖而不能加X鎖克婶。

持有X鎖的事務(wù)可讀可寫。

如果事務(wù)A對數(shù)據(jù)D加上X鎖后帕翻,其它事務(wù)不能再對D加鎖鸠补,直到A對D的鎖解除萝风。

表級鎖(Table Lock)和行級鎖(Row Lock)

從鎖的粒度角度嘀掸,主要分為表級鎖(Table Lock)和行級鎖(Row Lock)。

表級鎖將整個表加鎖规惰,性能開銷最小

用戶可以同時進行讀操作睬塌。當一個用戶對表進行寫操作時,用戶可以獲得一個寫鎖,寫鎖禁止其他的用戶讀寫操作揩晴。寫鎖比讀鎖的優(yōu)先級更高勋陪,即使有讀操作已排在隊列中,一個被申請的寫鎖仍可以排在所隊列的前列硫兰。

行級鎖僅對指定的記錄進行加鎖

這樣其它進程可以對同一個表中的其它記錄進行讀寫操作诅愚。行級鎖粒度最小,開銷大劫映,能夠支持高并發(fā)违孝,可能會出現(xiàn)死鎖。

MySQL的MyISAM引擎使用表級鎖泳赋,而InnoDB支持表級鎖和行級鎖雌桑,默認是行級鎖。
還有BDB引擎使用頁級鎖祖今,即一次鎖定一組記錄校坑,并發(fā)性介于行級鎖和表級鎖之間。

三級鎖協(xié)議

三級加鎖協(xié)議是為了保證正確的事務(wù)并發(fā)操作千诬,事務(wù)在讀耍目、寫數(shù)據(jù)庫對象是需要遵循的加鎖規(guī)則。

一級封鎖協(xié)議:事務(wù)T在修改數(shù)據(jù)R之前必須對它加X鎖徐绑,直到事務(wù)結(jié)束方可釋放制妄。而若事務(wù)T只是讀數(shù)據(jù),不進行修改泵三,則不需加鎖耕捞,因此一級加鎖協(xié)議下可能會出現(xiàn)臟讀和不可重復讀。

二級加鎖協(xié)議:在一級加鎖協(xié)議的基礎(chǔ)上烫幕,加上這樣一條規(guī)則——事務(wù)T在讀取數(shù)據(jù)R之前必須對它加S鎖俺抽,直到讀取完畢以后釋放。二級加鎖協(xié)議下可能會出現(xiàn)不可重復讀较曼。

三級加鎖協(xié)議:在一級加鎖協(xié)議的基礎(chǔ)上磷斧,加上這樣一條規(guī)則——事務(wù)T在讀取數(shù)據(jù)R之前必須對它加S鎖,直到事務(wù)結(jié)束方可釋放捷犹。三級加鎖協(xié)議避免了臟讀和不可重復讀的問題

Spring事務(wù)

Spring事務(wù)管理的實現(xiàn)有許多細節(jié)弛饭,如果對整個接口框架有個大體了解會非常有利于我們理解事務(wù)

image
image

Spring事務(wù)管理器

Spring事務(wù)管理涉及的接口的聯(lián)系如下:


image

Spring并不直接管理事務(wù)成玫,而是提供了多種事務(wù)管理器粟矿,他們將事務(wù)管理的職責委托給Hibernate或者JTA等持久化機制所提供的相關(guān)平臺框架的事務(wù)來實現(xiàn)。

Spring事務(wù)管理器的接口是org.springframework.transaction.PlatformTransactionManager迹辐,
通過這個接口枪孩,Spring為各個平臺如JDBC憔晒、Hibernate等都提供了對應(yīng)的事務(wù)管理器藻肄,

但是具體的實現(xiàn)就是各個平臺自己的事情了

/**
 * This is the central interface in Spring's transaction infrastructure.
 * Applications can use this directly, but it is not primarily meant as API:
 * Typically, applications will work with either TransactionTemplate or
 * declarative transaction demarcation through AOP.
 */
public interface PlatformTransactionManager {
    TransactionStatus getTransaction(TransactionDefinition definition)
        throws TransactionException;
    void commit(TransactionStatus status) throws TransactionException;
    void rollback(TransactionStatus status) throws TransactionException;
}

標準的jdbc處理事務(wù)代碼

Connection conn = DataSourceUtils.getConnection();
 //開啟事務(wù)
conn.setAutoCommit(false);
try {
    Object retVal = callback.doInConnection(conn);
    conn.commit(); //提交事務(wù)
    return retVal;
}catch (Exception e) {
    conn.rollback();//回滾事務(wù)
    throw e;
}finally {
    conn.close();
}

spring對應(yīng)的TranstactionTemplate處理

public class TransactionTemplate extends DefaultTransactionDefinition
        implements TransactionOperations, InitializingBean {
        
    @Override
    public <T> T execute(TransactionCallback<T> action) throws TransactionException {
        if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
            return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
        }
        else {
            TransactionStatus status = this.transactionManager.getTransaction(this);
            T result;
            try {
                result = action.doInTransaction(status);
            }
            catch (RuntimeException ex) {
                // Transactional code threw application exception -> rollback
                rollbackOnException(status, ex);
                throw ex;
            }
            catch (Error err) {
                // Transactional code threw error -> rollback
                rollbackOnException(status, err);
                throw err;
            }
            catch (Exception ex) {
                // Transactional code threw unexpected exception -> rollback
                rollbackOnException(status, ex);
                throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
            }
            this.transactionManager.commit(status);
            return result;
        }
    }
    
}

具體的事務(wù)管理機制對Spring來說是透明的,它并不關(guān)心那些拒担,那些是對應(yīng)各個平臺需要關(guān)心的嘹屯,所以Spring事務(wù)管理的一個優(yōu)點就是為不同的事務(wù)API提供一致的編程模型,如JTA从撼、JDBC州弟、Hibernate、JPA低零。下面分別介紹各個平臺框架實現(xiàn)事務(wù)管理的機制呆馁。

image

JDBC事務(wù)

如果應(yīng)用程序中直接使用JDBC來進行持久化,DataSourceTransactionManager會為你處理事務(wù)邊界毁兆。為了使用DataSourceTransactionManager浙滤,你需要使用如下的XML將其裝配到應(yīng)用程序的上下文定義中:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

實際上,DataSourceTransactionManager是通過調(diào)用java.sql.Connection來管理事務(wù)气堕,而后者是通過DataSource獲取到的纺腊。通過調(diào)用連接的commit()方法來提交事務(wù),同樣茎芭,事務(wù)失敗則通過調(diào)用rollback()方法進行回滾揖膜。

Hibernate事務(wù)

如果應(yīng)用程序的持久化是通過Hibernate實習的,那么你需要使用HibernateTransactionManager梅桩。對于Hibernate3壹粟,需要在Spring上下文定義中添加如下的<bean>聲明:

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

sessionFactory屬性需要裝配一個Hibernate的session工廠,HibernateTransactionManager的實現(xiàn)細節(jié)是它將事務(wù)管理的職責委托給org.hibernate.Transaction對象宿百,而后者是從Hibernate Session中獲取到的趁仙。當事務(wù)成功完成時,HibernateTransactionManager將會調(diào)用Transaction對象的commit()方法垦页,反之雀费,將會調(diào)用rollback()方法。

事務(wù)屬性

事務(wù)管理器接口PlatformTransactionManager通過getTransaction(TransactionDefinition definition)方法來得到事務(wù)痊焊,這個方法里面的參數(shù)是TransactionDefinition類盏袄,這個類就定義了一些基本的事務(wù)屬性。

public interface TransactionDefinition {
    int getPropagationBehavior(); // 返回事務(wù)的傳播行為
    int getIsolationLevel(); // 返回事務(wù)的隔離級別薄啥,事務(wù)管理器根據(jù)它來控制另外一個事務(wù)可以看到本事務(wù)內(nèi)的哪些數(shù)據(jù)
    int getTimeout();  // 返回事務(wù)必須在多少秒內(nèi)完成
    boolean isReadOnly(); // 事務(wù)是否只讀辕羽,事務(wù)管理器能夠根據(jù)這個返回值進行優(yōu)化,確保事務(wù)是只讀的
} 

那么什么是事務(wù)屬性呢垄惧?事務(wù)屬性可以理解成事務(wù)的一些基本配置刁愿,描述了事務(wù)策略如何應(yīng)用到方法上。事務(wù)屬性包含了5個方面

image

傳播行為

事務(wù)的第一個方面是傳播行為(propagation behavior)赘艳。
當事務(wù)方法被另一個事務(wù)方法調(diào)用時酌毡,必須指定事務(wù)應(yīng)該如何傳播克握。

為什么需要定義傳播蕾管?

在我們用SSH開發(fā)項目的時候枷踏,我們一般都是將事務(wù)設(shè)置在Service層 那么當我們調(diào)用Service層的一個方法的時候它能夠保證我們的這個方法中執(zhí)行的所有的對數(shù)據(jù)庫的更新操作保持在一個事務(wù)中,在事務(wù)層里面調(diào)用的這些方法要么全部成功掰曾,要么全部失敗旭蠕。那么事務(wù)的傳播特性也是從這里說起的。
如果你在你的Service層的這個方法中旷坦,除了調(diào)用了Dao層的方法之外掏熬,還調(diào)用了本類的其他的Service方法,那么在調(diào)用其他的Service方法的時候秒梅,這個事務(wù)是怎么規(guī)定的呢旗芬,我必須保證我在我方法里掉用的這個方法與我本身的方法處在同一個事務(wù)中,否則如果保證事物的一致性捆蜀。事務(wù)的傳播特性就是解決這個問題的疮丛,“事務(wù)是會傳播的”在Spring中有針對傳播特性的多種配置我們大多數(shù)情況下只用其中的一種:PROPGATION_REQUIRED:這個配置項的意思是說當我調(diào)用service層的方法的時候開啟一個事務(wù)(具體調(diào)用那一層的方法開始創(chuàng)建事務(wù),要看你的aop的配置),那么在調(diào)用這個service層里面的其他的方法的時候,如果當前方法產(chǎn)生了事務(wù)就用當前方法產(chǎn)生的事務(wù)辆它,否則就創(chuàng)建一個新的事務(wù)誊薄。這個工作使由Spring來幫助我們完成的。
以前沒有Spring幫助我們完成事務(wù)的時候我們必須自己手動的控制事務(wù)锰茉,例如當我們項目中僅僅使用hibernate呢蔫,而沒有集成進spring的時候,我們在一個service層中調(diào)用其他的業(yè)務(wù)邏輯方法飒筑,為了保證事物必須也要把當前的hibernate session傳遞到下一個方法中片吊,或者采用ThreadLocal的方法,將session傳遞給下一個方法协屡,其實都是一個目的《瘢現(xiàn)在這個工作由spring來幫助我們完成,就可以讓我們更加的專注于我們的業(yè)務(wù)邏輯著瓶。而不用去關(guān)心事務(wù)的問題联予。

Spring定義了七種傳播行為:

PROPAGATION_REQUIRED

如果當前沒有事務(wù),就新建一個事務(wù)材原,如果已經(jīng)存在一個事務(wù)中沸久,加入到這個事務(wù)中。這是最常見的選擇余蟹。

PROPAGATION_SUPPORTS

支持當前事務(wù)卷胯,如果當前沒有事務(wù),就以非事務(wù)方式執(zhí)行威酒。

PROPAGATION_MANDATORY

使用當前的事務(wù)窑睁,如果當前沒有事務(wù)挺峡,就拋出異常。

PROPAGATION_REQUIRES_NEW

新建事務(wù)担钮,如果當前存在事務(wù)橱赠,把當前事務(wù)掛起。

PROPAGATION_NOT_SUPPORTED

以非事務(wù)方式執(zhí)行操作箫津,如果當前存在事務(wù)狭姨,就把當前事務(wù)掛起。

PROPAGATION_NEVER

以非事務(wù)方式執(zhí)行苏遥,如果當前存在事務(wù)饼拍,則拋出異常。

PROPAGATION_NESTED

如果當前存在事務(wù)田炭,則在嵌套事務(wù)內(nèi)執(zhí)行师抄。如果當前沒有事務(wù),則執(zhí)行與PROPAGATION_REQUIRED類似的操作教硫。

傳播行為詳細

通過實例嘗試一下各個傳播屬性

ServiceA {
       
     void methodA() {
         ServiceB.methodB();
     }
}
  
ServiceB {
       
     void methodB() {
     }
}
PROPAGATION_REQUIRED

如果存在一個事務(wù)叨吮,則支持當前事務(wù)。如果沒有事務(wù)則開啟一個新的事務(wù)

//事務(wù)屬性 PROPAGATION_REQUIRED
methodA{
    ……
    methodB();
    ……
}
//事務(wù)屬性 PROPAGATION_REQUIRED
methodB{
   ……
}

單獨調(diào)用methodB方法:

main{ 
    metodB(); 
}

相當于

Main{ 
    Connection con=null; 
    try{ 
        con = getConnection(); 
        con.setAutoCommit(false); 

        //方法調(diào)用
        methodB(); 

        //提交事務(wù)
        con.commit(); 
    } Catch(RuntimeException ex) { 
        //回滾事務(wù)
        con.rollback();   
    } finally { 
        //釋放資源
        closeCon(); 
    } 
} 

Spring保證在methodB方法中所有的調(diào)用都獲得到一個相同的連接栋豫。在調(diào)用methodB時挤安,沒有一個存在的事務(wù),所以獲得一個新的連接丧鸯,開啟了一個新的事務(wù)蛤铜。

只是在ServiceB.methodB內(nèi)的任何地方出現(xiàn)異常,ServiceB.methodB將會被回滾丛肢,不會引起ServiceA.methodA的回滾

單獨調(diào)用MethodA時围肥,在MethodA內(nèi)又會調(diào)用MethodB.

執(zhí)行效果相當于:

main{ 
    Connection con = null; 
    try{ 
        con = getConnection(); 
        methodA(); 
        con.commit(); 
    } catch(RuntimeException ex) { 
        con.rollback(); 
    } finally {    
        closeCon(); 
    }  
} 

調(diào)用MethodA時,環(huán)境中沒有事務(wù)蜂怎,所以開啟一個新的事務(wù).

當在MethodA中調(diào)用MethodB時穆刻,環(huán)境中已經(jīng)有了一個事務(wù),所以methodB就加入當前事務(wù)

ServiceA.methodA或者ServiceB.methodB無論哪個發(fā)生異常methodA和methodB作為一個整體都將一起回滾

PROPAGATION_SUPPORTS

如果存在一個事務(wù)杠步,支持當前事務(wù)氢伟。如果沒有事務(wù),則非事務(wù)的執(zhí)行幽歼。但是對于事務(wù)同步的事務(wù)管理器朵锣,PROPAGATION_SUPPORTS與不使用事務(wù)有少許不同

//事務(wù)屬性 PROPAGATION_REQUIRED
methodA(){
  methodB();
}

//事務(wù)屬性 PROPAGATION_SUPPORTS
methodB(){
  ……
}

單純的調(diào)用methodB時,methodB方法是非事務(wù)的執(zhí)行的甸私。當調(diào)用methdA時,methodB則加入了methodA的事務(wù)中,事務(wù)地執(zhí)行

PROPAGATION_MANDATORY

如果已經(jīng)存在一個事務(wù)诚些,支持當前事務(wù)。如果沒有一個活動的事務(wù)皇型,則拋出異常

//事務(wù)屬性 PROPAGATION_REQUIRED
methodA(){
    methodB();
}

//事務(wù)屬性 PROPAGATION_MANDATORY
    methodB(){
    ……
}

當單獨調(diào)用methodB時诬烹,因為當前沒有一個活動的事務(wù)砸烦,則會拋出異常throw new IllegalTransactionStateException(“Transaction propagation ‘mandatory’ but no existing transaction found”);

當調(diào)用methodA時,methodB則加入到methodA的事務(wù)中绞吁,事務(wù)地執(zhí)行

PROPAGATION_REQUIRES_NEW

總是開啟一個新的事務(wù)幢痘。如果一個事務(wù)已經(jīng)存在,則將這個存在的事務(wù)掛起掀泳。

//事務(wù)屬性 PROPAGATION_REQUIRED
methodA(){
    doSomeThingA();
    methodB();
    doSomeThingB();
}

//事務(wù)屬性 PROPAGATION_REQUIRES_NEW
methodB(){
    ……
}

調(diào)用A方法:

main(){
    methodA();
}

相當于

main(){
    TransactionManager tm = null;
    try{
        //獲得一個JTA事務(wù)管理器
        tm = getTransactionManager();
        tm.begin();//開啟一個新的事務(wù)
        Transaction ts1 = tm.getTransaction();
        doSomeThing();
        tm.suspend();//掛起當前事務(wù)
        try{
            tm.begin();//重新開啟第二個事務(wù)
            Transaction ts2 = tm.getTransaction();
            methodB();
            ts2.commit();//提交第二個事務(wù)
        } Catch(RunTimeException ex) {
            ts2.rollback();//回滾第二個事務(wù)
        } finally {
            //釋放資源
        }
        //methodB執(zhí)行完后雪隧,恢復第一個事務(wù)
        tm.resume(ts1);
        doSomeThingB();
        ts1.commit();//提交第一個事務(wù)
    } catch(RunTimeException ex) {
        ts1.rollback();//回滾第一個事務(wù)
    } finally {
        //釋放資源
    }
}

在這里西轩,我把ts1稱為外層事務(wù)员舵,ts2稱為內(nèi)層事務(wù)。從上面的代碼可以看出藕畔,ts2與ts1是兩個獨立的事務(wù)马僻,互不相干。

Ts2是否成功并不依賴于ts1

如果methodA方法在調(diào)用methodB方法后的doSomeThingB方法失敗了注服,而methodB方法所做的結(jié)果依然被提交韭邓。

而除了 methodB之外的其它代碼導致的結(jié)果卻被回滾了

PROPAGATION_NOT_SUPPORTED

以非事務(wù)方式執(zhí)行操作,如果當前存在事務(wù)溶弟,就把當前事務(wù)掛起,

//事務(wù)屬性 PROPAGATION_REQUIRED
methodA(){
    doSomeThingA();
    methodB();
    doSomeThingB();
}

//事務(wù)屬性 PROPAGATION_NOT_SUPPORTED
methodB(){
    ……
}

當前不支持事務(wù)女淑。比如ServiceA.methodA的事務(wù)級別是PROPAGATION_REQUIRED ,
而ServiceB.methodB的事務(wù)級別是PROPAGATION_NOT_SUPPORTED 辜御,
那么當執(zhí)行到ServiceB.methodB時鸭你,ServiceA.methodA的事務(wù)掛起,而他以非事務(wù)的狀態(tài)運行完擒权,再繼續(xù)ServiceA.methodA的事務(wù)

PROPAGATION_NEVER

不能在事務(wù)中運行袱巨。假設(shè)ServiceA.methodA的事務(wù)級別是PROPAGATION_REQUIRED,
而ServiceB.methodB的事務(wù)級別是PROPAGATION_NEVER 碳抄,
那么ServiceB.methodB就要拋出異常了愉老。

PROPAGATION_NESTED

開始一個 "嵌套的" 事務(wù), 它是已經(jīng)存在事務(wù)的一個真正的子事務(wù). 潛套事務(wù)開始執(zhí)行時, 它將取得一個 savepoint. 如果這個嵌套事務(wù)失敗, 我們將回滾到此 savepoint. 潛套事務(wù)是外部事務(wù)的一部分, 只有外部事務(wù)結(jié)束后它才會被提交.

比如我們設(shè)計ServiceA.methodA的事務(wù)級別為PROPAGATION_REQUIRED,ServiceB.methodB的事務(wù)級別為PROPAGATION_NESTED剖效,那么當執(zhí)行到ServiceB.methodB的時候嫉入,ServiceA.methodA所在的事務(wù)就會掛起,ServiceB.methodB會起一個新的子事務(wù)并設(shè)置savepoint璧尸,等待ServiceB.methodB的事務(wù)完成以后咒林,他才繼續(xù)執(zhí)行

因為ServiceB.methodB是外部事務(wù)的子事務(wù),那么

  1. 如果ServiceB.methodB已經(jīng)提交逗宁,那么ServiceA.methodA失敗回滾映九,ServiceB.methodB也將回滾。
  2. 如果ServiceB.methodB失敗回滾瞎颗,如果他拋出的異常被ServiceA.methodA的try..catch捕獲并處理件甥,ServiceA.methodA事務(wù)仍然可能提交捌议;如果他拋出的異常未被ServiceA.methodA捕獲處理,ServiceA.methodA事務(wù)將回滾引有。

理解Nested的關(guān)鍵是savepoint瓣颅。

與PROPAGATION_REQUIRES_NEW的區(qū)別

  1. RequiresNew每次都創(chuàng)建新的獨立的物理事務(wù),而Nested只有一個物理事務(wù)譬正;
  2. Nested嵌套事務(wù)回滾或提交不會導致外部事務(wù)回滾或提交宫补,但外部事務(wù)回滾將導致嵌套事務(wù)回滾,而 RequiresNew由于都是全新的事務(wù)曾我,所以之間是無關(guān)聯(lián)的粉怕;
  3. Nested使用JDBC 3的保存點實現(xiàn),即如果使用低版本驅(qū)動將導致不支持嵌套事務(wù)抒巢。
    使用嵌套事務(wù)贫贝,必須確保具體事務(wù)管理器實現(xiàn)的nestedTransactionAllowed屬性為true,否則不支持嵌套事務(wù)蛉谜,如DataSourceTransactionManager默認支持稚晚,而HibernateTransactionManager默認不支持,需要我們來開啟型诚。

在 spring 中使用 PROPAGATION_NESTED的前提:

  1. 我們要設(shè)置 transactionManager 的 nestedTransactionAllowed 屬性為 true, 注意, 此屬性默認為 false!!!
  2. java.sql.Savepoint 必須存在, 即 jdk 版本要 1.4+
  3. Connection.getMetaData().supportsSavepoints() 必須為 true, 即 jdbc drive 必須支持 JDBC 3.0
image

隔離規(guī)則

用來解決并發(fā)事務(wù)時出現(xiàn)的問題客燕,其使用TransactionDefinition中的靜態(tài)變量來指定

  1. ISOLATION_DEFAULT 使用后端數(shù)據(jù)庫默認的隔離級別
  2. ISOLATION_READ_UNCOMMITTED 最低的隔離級別,允許讀取尚未提交的數(shù)據(jù)變更狰贯,可能會導致臟讀也搓、幻讀或不可重復讀
  3. ISOLATION_READ_COMMITTED 允許讀取并發(fā)事務(wù)已經(jīng)提交的數(shù)據(jù),可以阻止臟讀暮现,但是幻讀或不可重復讀仍有可能發(fā)生
  4. ISOLATION_REPEATABLE_READ 對同一字段的多次讀取結(jié)果都是一致的还绘,除非數(shù)據(jù)是被本身事務(wù)自己所修改,可以阻止臟讀和不可重復讀栖袋,但幻讀仍有可能發(fā)生
  5. ISOLATION_SERIALIZABLE 最高的隔離級別拍顷,完全服從ACID的隔離級別,確保阻止臟讀塘幅、不可重復讀以及幻讀昔案,也是最慢的事務(wù)隔離級別,因為它通常是通過完全鎖定事務(wù)相關(guān)的數(shù)據(jù)庫表來實現(xiàn)的

可以使用DefaultTransactionDefinition類的setIsolationLevel(TransactionDefinition. ISOLATION_READ_COMMITTED)來指定隔離級別电媳,其中此處表示隔離級別為提交讀

也可以使用或setIsolationLevelName(“ISOLATION_READ_COMMITTED”)方式指定踏揣,其中參數(shù)就是隔離級別靜態(tài)變量的名字,但不推薦這種方式

事務(wù)只讀

將事務(wù)標識為只讀匾乓,只讀事務(wù)不修改任何數(shù)據(jù)捞稿;

對于JDBC只是簡單的將連接設(shè)置為只讀模式,對于更新將拋出異常;

對于一些其他ORM框架有一些優(yōu)化作用娱局,如在Hibernate中彰亥,Spring事務(wù)管理器將執(zhí)行“session.setFlushMode(FlushMode.MANUAL)”
即指定Hibernate會話在只讀事務(wù)模式下不用嘗試檢測和同步持久對象的狀態(tài)的更新。

如果使用設(shè)置具體事務(wù)管理的validateExistingTransaction屬性為true(默認false)衰齐,將確保整個事務(wù)傳播鏈都是只讀或都不是只讀


image

第二個addressService.save()不能設(shè)置成false

對于錯誤的事務(wù)只讀設(shè)置將拋出IllegalTransactionStateException異常任斋,并伴隨“Participating transaction with definition [……] is not marked as read-only……”信息,表示參與的事務(wù)只讀屬性設(shè)置錯誤

事務(wù)超時

設(shè)置事務(wù)的超時時間耻涛,單位為秒废酷,默認為-1表示使用底層事務(wù)的超時時間

使用如setTimeout(100)來設(shè)置超時時間,如果事務(wù)超時將拋出org.springframework.transaction.TransactionTimedOutException異常并將當前事務(wù)標記為應(yīng)該回滾抹缕,即超時后事務(wù)被自動回滾

可以使用具體事務(wù)管理器實現(xiàn)的defaultTimeout屬性設(shè)置默認的事務(wù)超時時間澈蟆,如DataSourceTransactionManager. setDefaultTimeout(10)

回滾規(guī)則

spring事務(wù)管理器會捕捉任何未處理的異常,然后依據(jù)規(guī)則決定是否回滾拋出異常的事務(wù)

默認配置下歉嗓,Spring只有在拋出的異常為運行時unchecked異常時才回滾該事務(wù)丰介,也就是拋出的異常為RuntimeException的子類(Errors也會導致事務(wù)回滾)背蟆,而拋出checked異常則不會導致事務(wù)回滾鉴分。可以明確的配置在拋出那些異常時回滾事務(wù)带膀,包括checked異常志珍。也可以明確定義那些異常拋出時不回滾事務(wù)

如何改變默認規(guī)則

  1. 讓checked例外也回滾:在整個方法前加上 @Transactional(rollbackFor=Exception.class)
  2. 讓unchecked例外不回滾: @Transactional(notRollbackFor=RunTimeException.class)
  3. 不需要事務(wù)管理的(只查詢的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)

事務(wù)狀態(tài)

上面講到的調(diào)用PlatformTransactionManager接口的getTransaction()的方法得到的是TransactionStatus接口的一個實現(xiàn),這個接口的內(nèi)容如下:

public interface TransactionStatus{
    boolean isNewTransaction(); // 是否是新的事物
    boolean hasSavepoint(); // 是否有恢復點
    void setRollbackOnly();  // 設(shè)置為只回滾
    boolean isRollbackOnly(); // 是否為只回滾
    boolean isCompleted; // 是否已完成
} 

可以發(fā)現(xiàn)這個接口描述的是一些處理事務(wù)提供簡單的控制事務(wù)執(zhí)行和查詢事務(wù)狀態(tài)的方法垛叨,在回滾或提交的時候需要應(yīng)用對應(yīng)的事務(wù)狀態(tài)

編程式和聲明式事務(wù)

Spring提供了對編程式事務(wù)和聲明式事務(wù)的支持伦糯,編程式事務(wù)允許用戶在代碼中精確定義事務(wù)的邊界

而聲明式事務(wù)(基于AOP)有助于用戶將操作與事務(wù)規(guī)則進行解耦。

簡單地說嗽元,編程式事務(wù)侵入到了業(yè)務(wù)代碼里面敛纲,但是提供了更加詳細的事務(wù)管理;而聲明式事務(wù)由于基于AOP剂癌,所以既能起到事務(wù)管理的作用淤翔,又可以不影響業(yè)務(wù)代碼的具體實現(xiàn)。

編程式

Spring提供兩種方式的編程式事務(wù)管理佩谷,分別是:使用TransactionTemplate和直接使用PlatformTransactionManager

使用TransactionTemplate

采用TransactionTemplate和采用其他Spring模板旁壮,如JdbcTempalte和HibernateTemplate是一樣的方法。它使用回調(diào)方法谐檀,把應(yīng)用程序從處理取得和釋放資源中解脫出來抡谐。如同其他模板,TransactionTemplate是線程安全的桐猬。代碼片段:

    TransactionTemplate tt = new TransactionTemplate(); // 新建一個TransactionTemplate
    Object result = tt.execute(
        new TransactionCallback(){  
            public Object doTransaction(TransactionStatus status){  
                updateOperation();  
                return resultOfUpdateOperation();  
            }  
    }); // 執(zhí)行execute方法進行事務(wù)管理

使用TransactionCallback()可以返回一個值麦撵。如果使用TransactionCallbackWithoutResult則沒有返回值

使用PlatformTransactionManager

示例代碼如下:

DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
//定義一個某個框架平臺的TransactionManager,如JDBC、Hibernate
dataSourceTransactionManager.setDataSource(this.getJdbcTemplate().getDataSource()); // 設(shè)置數(shù)據(jù)源
    DefaultTransactionDefinition transDef = new DefaultTransactionDefinition(); // 定義事務(wù)屬性
    transDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED); // 設(shè)置傳播行為屬性
    TransactionStatus status = dataSourceTransactionManager.getTransaction(transDef); // 獲得事務(wù)狀態(tài)
    try {
        // 數(shù)據(jù)庫操作
        dataSourceTransactionManager.commit(status);// 提交
    } catch (Exception e) {
        dataSourceTransactionManager.rollback(status);// 回滾
    }

聲明式

有幾種實現(xiàn)方式免胃,不一一羅列了

使用tx攔截器

<!-- 定義事務(wù)管理器(聲明式的事務(wù)) --> 
    <bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED" />
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <aop:pointcut id="interceptorPointCuts"
            expression="execution(* com.bluesky.spring.dao.*.*(..))" />
        <aop:advisor advice-ref="txAdvice"
            pointcut-ref="interceptorPointCuts" />       
    </aop:config> 

全注解

<tx:annotation-driven transaction-manager="transactionManager" />
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager ">
        <property name="dataSource">
            <ref bean="basicDataSource" />
        </property>
</bean>

Spring源碼片段

在《BeanPostProcessor學習》中提到了AOP的實現(xiàn)方式五垮,聲明式事務(wù)實現(xiàn)是基于AOP

首先得解析xml配置,TxNamespaceHandler

@Override
    public void init() {
        registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
        registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
        registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
    }

主要是TransactionInterceptor類

public Object invoke(final MethodInvocation invocation) throws Throwable {
        // Work out the target class: may be {@code null}.
        // The TransactionAttributeSource should be passed the target class
        // as well as the method, which may be from an interface.
        Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

        // Adapt to TransactionAspectSupport's invokeWithinTransaction...
        //主要邏輯在父類
        return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
            @Override
            public Object proceedWithInvocation() throws Throwable {
                return invocation.proceed();
            }
        });
    }

核心邏輯杜秸,還得看父類TransactionAspectSupport#invokeWithinTransaction

邏輯主干很清晰

if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
            // Standard transaction demarcation with getTransaction and commit/rollback calls.
            // 判斷創(chuàng)建Transaction
            TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
            Object retVal = null;
            try {
                // This is an around advice: Invoke the next interceptor in the chain.
                // This will normally result in a target object being invoked.
                //執(zhí)行業(yè)務(wù)邏輯
                retVal = invocation.proceedWithInvocation();
            }
            catch (Throwable ex) {
                // target invocation exception
                // 出現(xiàn)異常放仗,回滾
                completeTransactionAfterThrowing(txInfo, ex);
                throw ex;
            }
            finally {
                //清除當前事務(wù)狀態(tài)
                cleanupTransactionInfo(txInfo);
            }
            //提交事務(wù)
            commitTransactionAfterReturning(txInfo);
            return retVal;
        }

創(chuàng)建事務(wù)createTransactionIfNecessary

主要邏輯在PlatformTransactionManager#getTransaction()

public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
        //得到各個不同數(shù)據(jù)源的事務(wù)對象,spring盡然沒有把transaction對象抽象出來撬碟,很是奇怪
        Object transaction = doGetTransaction();

        // Cache debug flag to avoid repeated checks.
        boolean debugEnabled = logger.isDebugEnabled();

        if (definition == null) {
            // Use defaults if no transaction definition given.
            definition = new DefaultTransactionDefinition();
        }

        //此事務(wù)是否已經(jīng)存在
        if (isExistingTransaction(transaction)) {
            // Existing transaction found -> check propagation behavior to find out how to behave.
            return handleExistingTransaction(definition, transaction, debugEnabled);
        }

        // Check definition settings for new transaction.
        if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
            throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
        }

        // No existing transaction found -> check propagation behavior to find out how to proceed.
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
            throw new IllegalTransactionStateException(
                    "No existing transaction found for transaction marked with propagation 'mandatory'");
        }
        //這三種都是新建事務(wù)
        else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            SuspendedResourcesHolder suspendedResources = suspend(null);
            if (debugEnabled) {
                logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
            }
            try {
                boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
                DefaultTransactionStatus status = newTransactionStatus(
                        definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
                //開始獲取鏈接诞挨,開啟事務(wù),綁定資源到當前線程
                doBegin(transaction, definition);
                prepareSynchronization(status, definition);
                return status;
            }
            catch (RuntimeException | Error ex) {
                resume(null, suspendedResources);
                throw ex;
            }
        }
        else {
            // Create "empty" transaction: no actual transaction, but potentially synchronization.
            if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
                logger.warn("Custom isolation level specified but no actual transaction initiated; " +
                        "isolation level will effectively be ignored: " + definition);
            }
            boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
            return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
        }
    }

TransactionStatus

這兒返回的是TransactionStatus

public interface TransactionStatus extends SavepointManager, Flushable {

    boolean isNewTransaction();

    boolean hasSavepoint();

    void setRollbackOnly();

    boolean isRollbackOnly();

    void flush();

    boolean isCompleted();

TransactionInfo

事務(wù)信息

protected final class TransactionInfo {

        private final PlatformTransactionManager transactionManager;

        private final TransactionAttribute transactionAttribute;

        private final String joinpointIdentification;

        private TransactionStatus transactionStatus;

        private TransactionInfo oldTransactionInfo;
    }
        

commitTransactionAfterReturning提交事務(wù)

邏輯到了AbstractPlatformTransactionManager#processRollback

private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
        try {
            boolean unexpectedRollback = unexpected;

            try {
                triggerBeforeCompletion(status);
                //有savepoint呢蛤,
                if (status.hasSavepoint()) {
                    if (status.isDebug()) {
                        logger.debug("Rolling back transaction to savepoint");
                    }
                    status.rollbackToHeldSavepoint();
                }
                else if (status.isNewTransaction()) {
                    if (status.isDebug()) {
                        logger.debug("Initiating transaction rollback");
                    }
                    //回滾事務(wù)
                    doRollback(status);
                }
                else {
                    // Participating in larger transaction
                    //在一個事務(wù)中惶傻,就先設(shè)置回滾標識,等父事務(wù)一起回滾
                    if (status.hasTransaction()) {
                        if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
                            if (status.isDebug()) {
                                logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
                            }
                            doSetRollbackOnly(status);
                        }
                        else {
                            if (status.isDebug()) {
                                logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
                            }
                        }
                    }
                    else {
                        logger.debug("Should roll back transaction but cannot - no transaction available");
                    }
                    // Unexpected rollback only matters here if we're asked to fail early
                    if (!isFailEarlyOnGlobalRollbackOnly()) {
                        unexpectedRollback = false;
                    }
                }
            }
            catch (RuntimeException | Error ex) {
                triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
                throw ex;
            }

            triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);

            // Raise UnexpectedRollbackException if we had a global rollback-only marker
            if (unexpectedRollback) {
                throw new UnexpectedRollbackException(
                        "Transaction rolled back because it has been marked as rollback-only");
            }
        }
        finally {
            cleanupAfterCompletion(status);
        }

completeTransactionAfterThrowing回滾事務(wù)

protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
        //有事務(wù)才能回滾
        if (txInfo != null && txInfo.hasTransaction()) {
            if (logger.isTraceEnabled()) {
                logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
                        "] after exception: " + ex);
            }
            //回滾在 (ex instanceof RuntimeException || ex instanceof Error)
            if (txInfo.transactionAttribute.rollbackOn(ex)) {
                try {
                    txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
                }
                catch (TransactionSystemException ex2) {
                    logger.error("Application exception overridden by rollback exception", ex);
                    ex2.initApplicationException(ex);
                    throw ex2;
                }
                catch (RuntimeException ex2) {
                    logger.error("Application exception overridden by rollback exception", ex);
                    throw ex2;
                }
                catch (Error err) {
                    logger.error("Application exception overridden by rollback error", ex);
                    throw err;
                }
            }
            else {
                // We don't roll back on this exception.
                // Will still roll back if TransactionStatus.isRollbackOnly() is true.
                try {
                    txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
                }
                catch (TransactionSystemException ex2) {
                    logger.error("Application exception overridden by commit exception", ex);
                    ex2.initApplicationException(ex);
                    throw ex2;
                }
                catch (RuntimeException ex2) {
                    logger.error("Application exception overridden by commit exception", ex);
                    throw ex2;
                }
                catch (Error err) {
                    logger.error("Application exception overridden by commit error", ex);
                    throw err;
                }
            }
        }
    }

總結(jié)

一個完整的事務(wù)介紹結(jié)束了∑湔希框架就是封裝一切银室,透明一切,簡化一切励翼。本質(zhì)的流程不會變

歡迎關(guān)注【碼農(nóng)戲碼】
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蜈敢,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子汽抚,更是在濱河造成了極大的恐慌抓狭,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,013評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件造烁,死亡現(xiàn)場離奇詭異否过,居然都是意外死亡,警方通過查閱死者的電腦和手機惭蟋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評論 2 382
  • 文/潘曉璐 我一進店門苗桂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人告组,你說我怎么就攤上這事煤伟。” “怎么了惹谐?”我有些...
    開封第一講書人閱讀 152,370評論 0 342
  • 文/不壞的土叔 我叫張陵持偏,是天一觀的道長。 經(jīng)常有香客問我氨肌,道長鸿秆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,168評論 1 278
  • 正文 為了忘掉前任怎囚,我火速辦了婚禮卿叽,結(jié)果婚禮上桥胞,老公的妹妹穿的比我還像新娘。我一直安慰自己考婴,他們只是感情好贩虾,可當我...
    茶點故事閱讀 64,153評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著沥阱,像睡著了一般缎罢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上考杉,一...
    開封第一講書人閱讀 48,954評論 1 283
  • 那天策精,我揣著相機與錄音,去河邊找鬼崇棠。 笑死咽袜,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的枕稀。 我是一名探鬼主播询刹,決...
    沈念sama閱讀 38,271評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼萎坷!你這毒婦竟也來了凹联?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,916評論 0 259
  • 序言:老撾萬榮一對情侶失蹤食铐,失蹤者是張志新(化名)和其女友劉穎匕垫,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體虐呻,經(jīng)...
    沈念sama閱讀 43,382評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,877評論 2 323
  • 正文 我和宋清朗相戀三年寞秃,在試婚紗的時候發(fā)現(xiàn)自己被綠了斟叼。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,989評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡春寿,死狀恐怖朗涩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情绑改,我是刑警寧澤谢床,帶...
    沈念sama閱讀 33,624評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站厘线,受9級特大地震影響识腿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜造壮,卻給世界環(huán)境...
    茶點故事閱讀 39,209評論 3 307
  • 文/蒙蒙 一渡讼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦成箫、人聲如沸展箱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽混驰。三九已至,卻和暖如春皂贩,著一層夾襖步出監(jiān)牢的瞬間账胧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評論 1 260
  • 我被黑心中介騙來泰國打工先紫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留治泥,地道東北人。 一個月前我還...
    沈念sama閱讀 45,401評論 2 352
  • 正文 我出身青樓遮精,卻偏偏與公主長得像居夹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子本冲,可洞房花燭夜當晚...
    茶點故事閱讀 42,700評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Spring事務(wù)機制主要包括聲明式事務(wù)和編程式事務(wù)准脂,此處側(cè)重講解聲明式事務(wù),編程式事務(wù)在實際開發(fā)中得不到廣泛使用檬洞,...
    EnigmaXXX閱讀 667評論 0 0
  • 1. 事務(wù)基礎(chǔ) 1.1 什么是事務(wù) 所謂事務(wù)就是用戶定義的一個數(shù)據(jù)庫操作序列狸膏,這些操作要么全做,要么全不做添怔,是一個...
    執(zhí)筆弄風月閱讀 503評論 0 0
  • 事務(wù)有四個特性:ACID 原子性(Atomicity):事務(wù)是一個原子操作湾戳,由一系列動作組成。事務(wù)的原子性確保動作...
    jiangmo閱讀 1,224評論 0 7
  • 一、事務(wù)的基本原理 Spring事務(wù)的本質(zhì)其實就是數(shù)據(jù)庫對事務(wù)的支持艾杏,沒有數(shù)據(jù)庫的事務(wù)支持韧衣,spring是無法提供...
    芭蕾武閱讀 1,693評論 3 12
  • 希望每個紀念日都是快樂的,但今天不快樂购桑。
    AmNobody閱讀 315評論 0 0