欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文詳解Spring事務(wù)的實現(xiàn)與本質(zhì)

 更新時間:2023年04月04日 08:49:41   作者:歸去來?兮  
這篇文章主要介紹了Spring中事務(wù)的兩種實現(xiàn)方式:聲明式事務(wù)、編程式事務(wù)以及他們的本質(zhì)。文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

一、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ù)嵌套場景下的部分回滾。

五、總結(jié)

這篇先介紹了事務(wù)的基礎(chǔ)知識,然后總結(jié)了Spring事務(wù)的支持方式,分析了他們的原理,最后總結(jié)下來就會發(fā)現(xiàn)Spring的事務(wù)其實全部都是依賴于數(shù)據(jù)源對數(shù)據(jù)庫的事務(wù)操作,若是數(shù)據(jù)庫事務(wù)不支持的動作,Spring也是不支持的。Spring事務(wù)的支持動作都是依賴于底層數(shù)據(jù)庫事務(wù)的封裝,包括了嵌套事務(wù)的場景,希望這一篇的總結(jié)可以幫助到路過的朋友。

到此這篇關(guān)于一文詳解Spring事務(wù)的實現(xiàn)與本質(zhì)的文章就介紹到這了,更多相關(guān)Spring事務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解Intellij IDEA的Facets和Artifacts

    詳解Intellij IDEA的Facets和Artifacts

    這篇文章主要介紹了Intellij IDEA的Facets和Artifacts的相關(guān)知識,本文通過實例給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2020-09-09
  • java網(wǎng)上圖書商城(8)訂單模塊3

    java網(wǎng)上圖書商城(8)訂單模塊3

    這篇文章主要為大家詳細介紹了java網(wǎng)上圖書商城,訂單模塊第三篇,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • java 簡單的計算器程序?qū)嵗a

    java 簡單的計算器程序?qū)嵗a

    這篇文章主要介紹了java 簡單的計算器程序?qū)嵗a的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • Java 創(chuàng)建PDF打印小冊子案例

    Java 創(chuàng)建PDF打印小冊子案例

    這篇文章主要給大家分享Java 創(chuàng)建PDF打印小冊子案例,PDF打印小冊子是指將PDF格式文檔在打印成刊物前需要提前進行的頁面排版,以便在打印后裝訂成冊,下面文章內(nèi)容我們將下面以Java代碼展示如何來實現(xiàn),需要的朋友可以參考一下
    2021-10-10
  • java基礎(chǔ)二叉搜索樹圖文詳解

    java基礎(chǔ)二叉搜索樹圖文詳解

    二叉樹是一種非常重要的數(shù)據(jù)結(jié)構(gòu),它同時具有數(shù)組和鏈表各自的特點,下面這篇文章主要給大家介紹了關(guān)于java基礎(chǔ)二叉搜索樹的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-03-03
  • Java中四種線程池的使用示例詳解

    Java中四種線程池的使用示例詳解

    這篇文章主要給大家介紹了關(guān)于Java中四種線程池的使用方法,四種線程池分別包括FixedThreadPool、CachedThreadPool、ScheduledThreadPool以及SingleThreadExecutor,文中給出了詳細的示例代碼供大家參考,需要的朋友們下面來一起看看吧。
    2017-08-08
  • Java內(nèi)存模型知識詳解

    Java內(nèi)存模型知識詳解

    這篇文章主要介紹了Java內(nèi)存模型知識詳解,文中通過對內(nèi)存訪問時的交互關(guān)系圖解介紹的十分詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • 從繁瑣到簡潔的Jenkins?Pipeline腳本優(yōu)化實踐

    從繁瑣到簡潔的Jenkins?Pipeline腳本優(yōu)化實踐

    這篇文章主要為大家介紹了從繁瑣到簡潔的Jenkins?Pipeline腳本優(yōu)化實踐示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-12-12
  • 詳解如何在spring boot中使用spring security防止CSRF攻擊

    詳解如何在spring boot中使用spring security防止CSRF攻擊

    這篇文章主要介紹了詳解如何在spring boot中使用spring security防止CSRF攻擊,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05
  • 使用CI/CD工具Github Action發(fā)布jar到Maven中央倉庫的詳細介紹

    使用CI/CD工具Github Action發(fā)布jar到Maven中央倉庫的詳細介紹

    今天通過對Github Action的簡單使用來介紹了CI/CD的作用,這個技術(shù)體系是項目集成交付的趨勢,也是面試中的一個亮點技能。 而且這種方式可以實現(xiàn)“一次配置,隨時隨地集成部署”,感興趣的朋友一起看看吧
    2021-07-07

最新評論