您好,登錄后才能下訂單哦!
這篇文章主要介紹了Spring事務(wù)的實現(xiàn)方法與本質(zhì)是什么的相關(guān)知識,內(nèi)容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Spring事務(wù)的實現(xiàn)方法與本質(zhì)是什么文章都會有所收獲,下面我們一起來看看吧。
先回憶下Spring事務(wù)的基礎(chǔ)知識事務(wù)的隔離級別與Spring事務(wù)的傳播機制。我們知道數(shù)據(jù)庫層面是不支持事務(wù)的傳播機制的,這個是Spring獨有的
臟讀:對于同一條數(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)了變化。
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)中效率非常低下。
事務(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ù)的支持方式一種常用的聲明式事務(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ū)別吧
使用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; } } }
聲明式事務(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ù)的實現(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),底層卻都是一致的。
這里總結(jié)兩個常見的事務(wù)問題事務(wù)的回滾和不回滾,以及事務(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)用回滾方法,從而達到了回滾的目的。
有沒有思考過這個問題: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è)資訊頻道。
免責(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)容。