溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Spring事務(wù)的實現(xiàn)方法與本質(zhì)是什么

發(fā)布時間:2023-04-04 10:24:34 來源:億速云 閱讀:97 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹了Spring事務(wù)的實現(xiàn)方法與本質(zhì)是什么的相關(guān)知識,內(nèi)容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Spring事務(wù)的實現(xiàn)方法與本質(zhì)是什么文章都會有所收獲,下面我們一起來看看吧。

    一、Spring事務(wù)的基礎(chǔ)知識

    先回憶下Spring事務(wù)的基礎(chǔ)知識事務(wù)的隔離級別與Spring事務(wù)的傳播機制。我們知道數(shù)據(jù)庫層面是不支持事務(wù)的傳播機制的,這個是Spring獨有的

    1.臟讀、不可重復(fù)讀、幻讀

    臟讀:對于同一條數(shù)據(jù)在一個事務(wù)中多次讀取的結(jié)果不一致,原因是第二次讀取數(shù)據(jù)時讀取的數(shù)據(jù)被其他未提交的事務(wù)修改了,當隔離級別是讀未提交時就會有這種問題存在。

    不可重復(fù)讀:對于同一條數(shù)據(jù)在一個事務(wù)中多次讀取的結(jié)果不一致,原因是第二次讀取數(shù)據(jù)時讀取的數(shù)據(jù)被其他已提交的事務(wù)修改了,當隔離級別是讀已提交時就會有這種問題存在。

    幻讀:臟讀和不可重復(fù)讀都是針對一條數(shù)據(jù)來說的,而我們使用重復(fù)讀的隔離級別就可以解決臟讀和不可重復(fù)讀的問題了,但是還是會有幻讀的問題,那什么是幻讀呢?幻讀指的是范圍查詢前后檢索結(jié)果不一致,比如說事務(wù)里第一次查詢customer表有10萬記錄,同一個事務(wù)第二次查詢時有11萬記錄,這就是幻讀?;米x產(chǎn)生的原因不是事務(wù)并發(fā)修改導(dǎo)致的(不可),而是查詢時有其他事務(wù)在做插入,導(dǎo)致了數(shù)據(jù)在量上出現(xiàn)了變化。

    2.事務(wù)的隔離級別

    Spring支持的事務(wù)隔離級別與數(shù)據(jù)的隔離級別其實沒有任何區(qū)別都是四種,說隔離級別必須要說事務(wù)的四大特性原子性、一致性、隔離性、持久性。需要拿出來說的便是隔離性,事務(wù)在支持隔離性時并不是將事務(wù)之間直接徹底隔離,而是給我們提供了幾個級別來劃分隔離的程度,也就是下面四種了。只有串行化才可以做到事務(wù)之間的完全隔離,而其他的隔離級別自然就會產(chǎn)生不同的問題了,因為事務(wù)之間有交叉。

    • 讀未提交:這是最低的隔離級別,相當于事務(wù)之間的隔離性基本沒有,所以這種隔離級別什么問題都解決不了,使用這種隔離級別會伴隨臟讀、不可重復(fù)讀、幻讀等問題。

    • 讀已提交:同一個事務(wù)里讀取的數(shù)據(jù)是其他事務(wù)里已經(jīng)提交的數(shù)據(jù),所以不會讀取到其他事務(wù)未提交的數(shù)據(jù),所有不會有臟讀的問題,但是還是可能發(fā)生不可重復(fù)讀、幻讀的問題。

    • 重復(fù)讀:同一個事務(wù)里支持重復(fù)讀取,也就是同一個事務(wù)里前后讀取的某條數(shù)據(jù)肯定一致(只針對某一條數(shù)據(jù)而言),但是仍然解決不了幻讀的問題,幻讀是因為并發(fā)插入導(dǎo)致的。重復(fù)讀只能解決并發(fā)修改的問題。

    • 串行化:串行化可以解決隔離性產(chǎn)生的所有問題,但是他的效率特別的低,所有任務(wù)都會排隊進行處理,在并發(fā)系統(tǒng)中效率非常低下。

    3.事務(wù)的傳播機制

    事務(wù)的傳播機制是Spring特有的機制,各個數(shù)據(jù)庫是不支持的,那Spring的傳播機制是什么呢,他們有什么作用呢?

    • PROPAGATION_REQUIRED(默認):如果當前方法沒有事務(wù),就創(chuàng)建一個新事務(wù);如果當前方法已經(jīng)有事務(wù),就加入到當前事務(wù)中。

    • PROPAGATION_SUPPORTS:如果當前方法有事務(wù),就加入到當前事務(wù)中;如果當前方法沒有事務(wù),就以非事務(wù)的方式執(zhí)行。

    • PROPAGATION_MANDATORY:如果當前方法有事務(wù),就加入到當前事務(wù)中;如果當前方法沒有事務(wù),就拋出異常。

    • PROPAGATION_REQUIRES_NEW:無論當前方法是否有事務(wù),都創(chuàng)建一個新事務(wù);如果當前方法已經(jīng)有事務(wù),就掛起當前事務(wù)。

    • PROPAGATION_NOT_SUPPORTED:以非事務(wù)的方式執(zhí)行當前方法;如果當前方法有事務(wù),就掛起當前事務(wù)。

    • PROPAGATION_NEVER:以非事務(wù)的方式執(zhí)行當前方法;如果當前方法有事務(wù),就拋出異常。

    • PROPAGATION_NESTED:在當前事務(wù)中創(chuàng)建一個嵌套事務(wù);如果當前方法沒有事務(wù),就相當于PROPAGATION_REQUIRED。

    二、Spring事務(wù)的實現(xiàn)方式

    Spring提供了兩種事務(wù)的支持方式一種常用的聲明式事務(wù),所謂聲明式事務(wù)就是我們直接使用注解聲明即可,而無需手動寫事務(wù)的開啟提交和回滾,這種事務(wù)的實現(xiàn)方式是AOP,AOP底層則是JDK的動態(tài)代理和CGLIB的動態(tài)代理。另一種支持的事務(wù)則是編程式事務(wù),這種實現(xiàn)方式則是直接編寫事務(wù)代碼,底層通過ORM框架調(diào)用到數(shù)據(jù)庫實現(xiàn)的事務(wù),編程式事務(wù)具有更加靈活的特點,同時Spring為我們提供了兩種編程式事務(wù)的實現(xiàn)方式,一種是TransactionTemplate,一種是PlatformTransactionManager。他們都能實現(xiàn)編程式事務(wù),不過使用TransactionTemplate無需我們手動提交或者回滾,Spring會根據(jù)異常拋出與否進行提交或者回滾。使用PlatformTransactionManager就需要我們自己提交或者回滾了。下面看下他們的實現(xiàn)區(qū)別吧

    1.編程式事務(wù)

    使用TransactionTemplate實現(xiàn)編程式事務(wù)

    下面是使用TransactionTemplate的偽代碼,我們可以為TransactionTemplate指明他的隔離級別和傳播機制,注意這里并沒有配置數(shù)據(jù)源相關(guān)操作,數(shù)據(jù)源仍需要單獨在配置文件中進行聲明數(shù)據(jù)源的類型和驅(qū)動類以及其他的數(shù)據(jù)庫訪問的賬號路徑超時時間最大連接等信息。

    @Component
    public class TestTransactionTemplate {
    
        
        private TransactionTemplate transactionTemplate;
        
    	@Inject
        public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
            this.transactionTemplate = transactionTemplate;
            transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
            transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        }
    
        public void transferMoney(final String fromAccount, final String toAccount, final double amount) {
            transactionTemplate.execute(new TransactionCallback<Void>() {
                public Void doInTransaction(TransactionStatus status) {
                    try {
                        // 執(zhí)行轉(zhuǎn)賬操作,將金額從fromAccount轉(zhuǎn)到toAccount
    //                    accountService.transfer(fromAccount, toAccount, amount);
                        // 如果沒有發(fā)生異常,則提交事務(wù)
                        return null;
                    } catch (Exception ex) {
                        // 如果發(fā)生異常,則回滾事務(wù)
                        status.setRollbackOnly();
                        throw new RuntimeException(ex);
                    }
                }
            });
        }
    }

    使用PlatformTransactionManager實現(xiàn)

    使用PlatformTransactionManager則必須我們手動進行提交或者回滾,下面是他的偽代碼

    @Component
    public class TestPlatformTransactionManager {
    
        PlatformTransactionManager transactionManager;
    
        @Inject
        public void setTransactionManager(PlatformTransactionManager transactionManager){
            this.transactionManager = transactionManager;
        }
    
        public void transfer(String fromAccount, String toAccount, double amount) {
            DefaultTransactionDefinition txDef = new DefaultTransactionDefinition();
            txDef.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
            txDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
            
            TransactionStatus txStatus = transactionManager.getTransaction(txDef);
            try {
                //執(zhí)行轉(zhuǎn)賬操作,將金額從fromAccount轉(zhuǎn)到toAccount
    //            accountService.transfer(fromAccount, toAccount, amount);
                //如果沒有發(fā)生異常,則提交事務(wù)
                transactionManager.commit(txStatus);
            } catch (Exception ex) {
                //如果發(fā)生異常,則回滾事務(wù)
                transactionManager.rollback(txStatus);
                throw ex;
            }
        }
    }

    2.聲明式事務(wù)

    聲明式事務(wù)底層利用AOP的方式對我們的事務(wù)對象進行代理,然后通過前后的增強操作實現(xiàn)了事務(wù)的管理,其實底層他們還是一樣的,使用聲明式事務(wù)的偽代碼如下,我們可以為注解聲明需要的各種屬性,常用的就是事務(wù)的傳播機制、隔離級別、超時時間、回滾異常、非回滾異常等。

    @Component
    public class TestTransactional {
    
        @Transactional(propagation = Propagation.REQUIRES_NEW //設(shè)置傳播機制
                ,isolation = Isolation.REPEATABLE_READ //設(shè)置隔離級別
                ,readOnly = false //設(shè)置是否只讀
                ,timeout = 30 //設(shè)置數(shù)據(jù)庫連接的超時時間
                ,transactionManager = "defaultTransactionManager" //設(shè)置事務(wù)管理器,一個工程多個時可以使用該方式
                ,rollbackFor = IllegalArgumentException.class // 指定回滾異常
                ,noRollbackFor = IndexOutOfBoundsException.class) // 指定非回滾異常
        public Boolean transfer(String fromAccount, String toAccount, double amount){
    
            // 業(yè)務(wù)操作...
    
            return Boolean.TRUE;
        }
    }

    三、Spring事務(wù)的本質(zhì)

    Spring雖然提供了多種事務(wù)的實現(xiàn)方式,其實最底層都是有一樣的。他都必須依賴數(shù)據(jù)源來對數(shù)據(jù)庫進行訪問,根據(jù)數(shù)據(jù)源來進行不同的封裝就實現(xiàn)了Spring的不同的事務(wù)實現(xiàn)方式。編程式事務(wù)是直接獲取數(shù)據(jù)源后進行手動操作,我們使用的PlatformTransactionManager、或者TransactionTemplate都是需要利用數(shù)據(jù)源來進行操作的。Spring通過數(shù)據(jù)源來和數(shù)據(jù)庫建立連接,開啟連接后我們就可以為這個連接設(shè)置他的隔離級別和一些超時信息等。這樣就會建立起一個事務(wù)了,最底層利用的還是數(shù)據(jù)庫的事務(wù)的動作。聲明式事務(wù)與編程式事務(wù)原理都是一致,只不過Spring通過AOP將我們的業(yè)務(wù)代碼進行了代理,產(chǎn)生了一個代理對象,具體使用什么代理技術(shù)Spring會根據(jù)我們的實現(xiàn)類進行選擇使用JDK還是CGLIB。產(chǎn)生的代理對象我們就可以在被Transactional注解修飾的方法的前后添加事務(wù)處理的相關(guān)代碼了,這個代碼和使用編程式事務(wù)的代碼區(qū)別不大。所以說Spring事務(wù)的本質(zhì)其實還是利用數(shù)據(jù)源打開和數(shù)據(jù)庫的連接,在連接上進行事務(wù)的操作。Spring根據(jù)不同需要又封裝了不同的事務(wù)實現(xiàn),底層卻都是一致的。

    四、Spring中事務(wù)常碰到的問題

    這里總結(jié)兩個常見的事務(wù)問題事務(wù)的回滾和不回滾,以及事務(wù)嵌套的場景

    1.事務(wù)回滾

    在不聲明回滾異常類時,只要被事務(wù)管理的方法發(fā)生異常,那么事務(wù)就是會回滾的。Spring根據(jù)拋出的異常來進行事務(wù)回滾。如果我們對異常進行了cath那Spring是無法進行事務(wù)回滾的,因為沒有異常拋出了。如果想要回滾我們可以手動聲明一個自定義異?;蛘咧付ǖ钠渌惓!_@樣就可以實現(xiàn)回滾。當然即使拋出了異常也不一定會回滾。這個還需要依賴我們指定的異常回滾類,一般可以為Transactional指明noRollBackFor。通過他可以指明在哪些異常下不回滾。通過rollBackFor指明哪些異常下回滾,需要滿足回滾異常時才會去回滾。

    那如果把異常catch了,又沒有拋出異常我們有沒有其他方式進行回滾呢(使用聲明式事務(wù)時)?其實還有一種方式進行回滾,如下所示:

    @Transactional
    public void someMethod() {
        
        if (condition) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }

    這種操作就是拿到當前事務(wù)的狀態(tài)手動更改為回滾狀態(tài),當執(zhí)行到AOP的后置增強時,就會調(diào)用回滾方法,從而達到了回滾的目的。

    2.事務(wù)嵌套

    有沒有思考過這個問題:Spring的事務(wù)的傳播機制到底是做什么用的呢?

    其實一般場景下Spring的事務(wù)傳播機制很少用得到,我們通常都是不顯示指定傳播機制的,而是使用默認的PROPAGATION_REQUIRED。默認的這個傳播機制就是有事務(wù)那我就使用你的事務(wù),如果沒有事務(wù)我就新建一個事務(wù)。假如有以下的場景存在,為了方便看就是用a、b命名方法了:

        @Transactional
        public void a(){ 
            //a方法被事務(wù)管理了,同時又調(diào)用了b這個事務(wù)方法
            b();
        }
        
        @Transactional
        public void b(){
    
        }

    在此時我們不為a、b兩個方法聲明傳播機制時,那a、b兩個方法其實是共用一個事務(wù)的,因為他們的事務(wù)傳播機制是PROPAGATION_REQUIRED。這個機制就是有事務(wù)就用已經(jīng)存在的,沒有則新建,很顯然a方法時開啟了一個事務(wù),執(zhí)行b方法時既然事務(wù)以及存在,就使用了a的事務(wù)。所以a、b方法其實是共用事務(wù)的?;乜吹谝徊糠諷pring中事務(wù)的傳播機制其實有7種,其實這其中主要就是為了事務(wù)嵌套場景下使用的,也就是我們事務(wù)中又調(diào)用了事務(wù)的場景。此時我們就需要關(guān)注內(nèi)層事務(wù)到底需要做什么,需不需要和上層事務(wù)保持一致的動作,如果不需要我們就可以選擇PROPAGATION_REQUIRED_NEW,這樣內(nèi)層事務(wù)就是一個全新的事務(wù)。此時Spring是通過數(shù)據(jù)源和數(shù)據(jù)庫新建立了一個連接,從而實現(xiàn)了新的事務(wù)開啟。

    此外在其中傳播機制中最后一種需要單獨說下:PROPAGATION_NESTED,他是嵌套事務(wù)。這個才是真正為嵌套事務(wù)使用的傳播機制。上面的例子中有內(nèi)層事務(wù)和外層事務(wù)其實他的原理還是不同的事務(wù)。而PROPAGATION_NESTED嵌套事務(wù)的底層卻是使用的一個事務(wù)實現(xiàn)的嵌套事務(wù)。此時上面的代碼可以改造如下:

        @Transactional(propagation = Propagation.REQUIRED)
        public void a(){
            //a方法被事務(wù)管理了,同時又調(diào)用了b這個事務(wù)方法
            b();
        }
    
        @Transactional(propagation = Propagation.NESTED)
        public void b(){
    
        }

    此時b方法就是一個嵌套事務(wù)了,Spring的嵌套事務(wù)同樣底層是依賴于數(shù)據(jù)庫的嵌套事務(wù),在Mysql里支持了一種偽嵌套事務(wù),就是通過在一個事務(wù)中保存回滾點savepoint的方式來進行事務(wù)嵌套。當事務(wù)正常提交時都會提交,當事務(wù)異常時我們可以指定事務(wù)回滾到指定的回滾點,下面列舉一個Mysql的回滾例子:假設(shè)有一個員工表employees表,對他進行了如下的操作:

    START TRANSACTION;
    SAVEPOINT sp1;
    
    INSERT INTO employees (id, name, age) VALUES (1, 'Alice', 30);
    
    SAVEPOINT sp2;
    
    INSERT INTO employees (id, name, age) VALUES (3, 'Bob', 25);
    
    SAVEPOINT sp3;
    
    INSERT INTO employees (id, name, age) VALUES (4, 'Charlie', 27);
    
    SAVEPOINT sp4;
    
    INSERT INTO employees (id, name, age) VALUES (5, 'Dave', 29);
    
    ROLLBACK TO sp3;
    COMMIT;

    上面的例子我們創(chuàng)建了4個回滾點,且我們最后是回滾到了sp3這個savepoint,那就意味著sp3之后的所有操作不會被寫入數(shù)據(jù)庫,而sp3之前的所有操作還是會正常入庫,這樣就實現(xiàn)了事務(wù)嵌套場景下的部分回滾機制。Spring事務(wù)傳播機制中的PROPAGATION_NESTED底層正是利用了Mysql的這一功能進行了事務(wù)嵌套場景下的部分回滾。

    關(guān)于“Spring事務(wù)的實現(xiàn)方法與本質(zhì)是什么”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對“Spring事務(wù)的實現(xiàn)方法與本質(zhì)是什么”知識都有一定的了解,大家如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

    向AI問一下細節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI