SpringBoot中的事務(wù)全方位詳解
SpringBoot事務(wù)的基本介紹
事務(wù)管理方式
在Spring中,事務(wù)有兩種實現(xiàn)方式,分別是編程式事務(wù)管理和聲明式事務(wù)管理兩種方式。
- 編程式事務(wù)管理: 編程式事務(wù)管理使用TransactionTemplate或者直接使用底層的PlatformTransactionManager。對于編程式事務(wù)管理,spring推薦使用TransactionTemplate。
- 聲明式事務(wù)管理: 建立在AOP之上的。其本質(zhì)是對方法前后進行攔截,然后在目標方法開始之前創(chuàng)建或者加入一個事務(wù),在執(zhí)行完目標方法之后根據(jù)執(zhí)行情況提交或者回滾事務(wù)。 聲明式事務(wù)管理不需要入侵代碼,通過@Transactional就可以進行事務(wù)操作,更快捷而且簡單,推薦使用。
編程式事務(wù)管理:
1、使用 TransactionTemplate 來管理事務(wù):
@Autowired
private TransactionTemplate transactionTemplate;
public void testTransaction() {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
try {
// .... 業(yè)務(wù)代碼
} catch (Exception e){
//回滾
transactionStatus.setRollbackOnly();
}
}
});
}2、使用 TransactionManager 來管理事務(wù):
@Autowired
private PlatformTransactionManager transactionManager;
public void testTransaction() {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
// .... 業(yè)務(wù)代碼
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
}
}聲明式事務(wù)管理:
@Transactional
public void testTransactional(){
}事務(wù)提交方式
默認情況下,數(shù)據(jù)庫處于自動提交模式。
每一條語句處于一個單獨的事務(wù)中,在這條語句執(zhí)行完畢時,如果執(zhí)行成功則隱式的提交事務(wù),如果執(zhí)行失敗則隱式的回滾事務(wù)。
對于正常的事務(wù)管理,是一組相關(guān)的操作處于一個事務(wù)之中,因此必須關(guān)閉數(shù)據(jù)庫的自動提交模式。
不過,這個我們不用擔心,spring會將底層連接的自動提交特性設(shè)置為false。
也就是在使用spring進行事物管理的時候,spring會將是否自動提交設(shè)置為false,等價于JDBC中的 connection.setAutoCommit(false);,在執(zhí)行完之后在進行提交,connection.commit(); 。
事務(wù)隔離級別
隔離級別是指若干個并發(fā)的事務(wù)之間的隔離程度。
TransactionDefinition 接口中定義了五個表示隔離級別的常量:
- TransactionDefinition.ISOLATION_DEFAULT:這是默認值,表示使用底層數(shù)據(jù)庫的默認隔離級別。對大部分數(shù)據(jù)庫而言,通常這值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED:該隔離級別表示一個事務(wù)可以讀取另一個事務(wù)修改但還沒有提交的數(shù)據(jù)。該級別不能防止臟讀,不可重復(fù)讀和幻讀,因此很少使用該隔離級別。比如PostgreSQL實際上并沒有此級別。
- TransactionDefinition.ISOLATION_READ_COMMITTED:該隔離級別表示一個事務(wù)只能讀取另一個事務(wù)已經(jīng)提交的數(shù)據(jù)。該級別可以防止臟讀,這也是大多數(shù)情況下的推薦值。
- TransactionDefinition.ISOLATION_REPEATABLE_READ:該隔離級別表示一個事務(wù)在整個過程中可以多次重復(fù)執(zhí)行某個查詢,并且每次返回的記錄都相同。該級別可以防止臟讀和不可重復(fù)讀。
- TransactionDefinition.ISOLATION_SERIALIZABLE:所有的事務(wù)依次逐個執(zhí)行,這樣事務(wù)之間就完全不可能產(chǎn)生干擾,也就是說,該級別可以防止臟讀、不可重復(fù)讀以及幻讀。但是這將嚴重影響程序的性能。通常情況下也不會用到該級別。
事務(wù)傳播行為
當事務(wù)方法被另外一個事務(wù)方法調(diào)用時,必須指定事務(wù)應(yīng)該如何傳播,例如,方法可能繼續(xù)在當前事務(wù)中執(zhí)行,也可以開啟一個新的事務(wù),在自己的事務(wù)中執(zhí)行。
- TransactionDefinition.PROPAGATION_REQUIRED:默認的事務(wù)傳播行為,指的是如果當前存在事務(wù),則加入該事務(wù);如果當前沒有事務(wù),則創(chuàng)建一個新的事務(wù)。更確切地意思是: 如果外部方法沒有開啟事務(wù)的話,Propagation.REQUIRED 修飾的內(nèi)部方法會開啟自己的事務(wù),且開啟的事務(wù)相互獨立,互不干擾。 如果外部方法開啟事務(wù)并且是 Propagation.REQUIRED 的話,所有 Propagation.REQUIRED 修飾的內(nèi)部方法和外部方法均屬于同一事務(wù) ,只要一個方法回滾,整個事務(wù)都需要回滾。
也就是說如果a方法和b方法都添加了注解,在默認傳播模式下,a方法內(nèi)部調(diào)用b方法,會把兩個方法的事務(wù)合并為一個事務(wù)。
- TransactionDefinition.PROPAGATION_REQUIRES_NEW:創(chuàng)建一個新的事務(wù),如果當前存在事務(wù),則把當前事務(wù)掛起。也就是說不管外部方法是否開啟事務(wù),Propagation.REQUIRES_NEW 修飾的內(nèi)部方法都會開啟自己的事務(wù),且開啟的事務(wù)與外部的事務(wù)相互獨立,互不干擾。當類A中的 a 方法用默認 Propagation.REQUIRED模式,類B中的 b方法加上采用 Propagation.REQUIRES_NEW模式,然后在 a 方法中調(diào)用 b方法操作數(shù)據(jù)庫,然而 a方法拋出異常后,b方法并沒有進行回滾,因為Propagation.REQUIRES_NEW會暫停 a方法的事務(wù) ,總結(jié)就是a不影響b,b影響a
- TransactionDefinition.PROPAGATION_SUPPORTS:如果當前存在事務(wù),則加入該事務(wù);如果當前沒有事務(wù),則以非事務(wù)的方式繼續(xù)運行。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務(wù)方式運行,如果當前存在事務(wù),則把當前事務(wù)掛起。
- TransactionDefinition.PROPAGATION_NEVER:以非事務(wù)方式運行,如果當前存在事務(wù),則拋出異常。
- TransactionDefinition.PROPAGATION_MANDATORY:如果當前存在事務(wù),則加入該事務(wù);如果當前沒有事務(wù),則拋出異常。
- TransactionDefinition.PROPAGATION_NESTED:如果當前存在事務(wù),則創(chuàng)建一個事務(wù)作為當前事務(wù)的嵌套事務(wù)來運行;如果當前沒有事務(wù),則該取值等價于TransactionDefinition.PROPAGATION_REQUIRED。
事務(wù)回滾規(guī)則
指示spring事務(wù)管理器回滾一個事務(wù)的推薦方法是在當前事務(wù)的上下文內(nèi)拋出異常。
spring事務(wù)管理器會捕捉任何未處理的異常,然后依據(jù)規(guī)則決定是否回滾拋出異常的事務(wù)。
默認配置下,spring只有在拋出的異常為運行時unchecked異常時才回滾該事務(wù),也就是拋出的異常為RuntimeException的子類(Errors也會導致事務(wù)回滾),而拋出checked異常則不會導致事務(wù)回滾。
可以明確的配置在拋出那些異常時回滾事務(wù),包括checked異常。
也可以明確定義那些異常拋出時不回滾事務(wù)。
事務(wù)常用配置
- readOnly:該屬性用于設(shè)置當前事務(wù)是否為只讀事務(wù),設(shè)置為true表示只讀,false則表示可讀寫,默認值為false。例如:@Transactional(readOnly=true);
- rollbackFor: 該屬性用于設(shè)置需要進行回滾的異常類數(shù)組,當方法中拋出指定異常數(shù)組中的異常時,則進行事務(wù)回滾。例如:指定單一異常類:@Transactional(rollbackFor=RuntimeException.class)指定多個異常類:@Transactional(rollbackFor={RuntimeException.class, Exception.class});
- rollbackForClassName: 該屬性用于設(shè)置需要進行回滾的異常類名稱數(shù)組,當方法中拋出指定異常名稱數(shù)組中的異常時,則進行事務(wù)回滾。例如:指定單一異常類名稱@Transactional(rollbackForClassName=”RuntimeException”)指定多個異常類名稱:@Transactional(rollbackForClassName={“RuntimeException”,”Exception”})。
- noRollbackFor:該屬性用于設(shè)置不需要進行回滾的異常類數(shù)組,當方法中拋出指定異常數(shù)組中的異常時,不進行事務(wù)回滾。例如:指定單一異常類:@Transactional(noRollbackFor=RuntimeException.class)指定多個異常類:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})。
- noRollbackForClassName:該屬性用于設(shè)置不需要進行回滾的異常類名稱數(shù)組,當方法中拋出指定異常名稱數(shù)組中的異常時,不進行事務(wù)回滾。例如:指定單一異常類名稱:@Transactional(noRollbackForClassName=”RuntimeException”)指定多個異常類名稱:@Transactional(noRollbackForClassName={“RuntimeException”,”Exception”})。
- propagation : 該屬性用于設(shè)置事務(wù)的傳播行為。例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)。
- isolation:該屬性用于設(shè)置底層數(shù)據(jù)庫的事務(wù)隔離級別,事務(wù)隔離級別用于處理多事務(wù)并發(fā)的情況,通常使用數(shù)據(jù)庫的默認隔離級別即可,基本不需要進行設(shè)置。
- timeout:該屬性用于設(shè)置事務(wù)的超時秒數(shù),默認值為-1表示永不超時。也就是指一個事務(wù)所允許執(zhí)行的最長時間,如果在超時時間內(nèi)還沒有完成的話,就自動回滾。假如事務(wù)的執(zhí)行時間格外的長,由于事務(wù)涉及到對數(shù)據(jù)庫的鎖定,就會導致長時間運行的事務(wù)占用數(shù)據(jù)庫資源。
@Transaction失效場景
1、訪問權(quán)限問題 (只有public方法會生效)
java的訪問權(quán)限主要有四種:private、default、protected、public,它們的權(quán)限從左到右,依次變大。spring要求被代理方法必須得是public的。
我們自定義的事務(wù)方法如果它的訪問權(quán)限不是public,會導致事務(wù)失效。
@Transactional
private void testTransactional() {
}2、方法用final修飾,不會生效
有時候,某個方法不想被子類重新,這時可以將該方法定義成final的。
普通方法這樣定義是沒問題的,但如果將事務(wù)方法定義成final,會導致事務(wù)失效
@Transactional
public final void testTransactional() {
}3、同一個類中的方法直接內(nèi)部調(diào)用,會導致事務(wù)失效
@Service
public class TransactionalTest implements TransactionalService{
@Override
public void testTransactional() {
transactional();
}
@Transactional
public void transactional(){}
}文章開頭說道,聲明式事務(wù)管理是建立在AOP之上的。AOP的實現(xiàn)原理是動態(tài)代理。
一個方法調(diào)用本類的其他方法是不會走代理,原因是在InvocationHandlerImpl#invoke中method.invoke(subject, args);
這里調(diào)用的是目標類subject的方法,直接執(zhí)行目標類方法,不會執(zhí)行代理類的方法。?
因為JDK動態(tài)代理采用的是接口實現(xiàn)的方式,通過反射調(diào)用目標類的方法,此時如果調(diào)用本類的方法,this指的是目標類,并不是代理類所以不會走代理。
不走代理,事務(wù)自然會失效。
編寫新的sevice
這個方法非常簡單,只需要新加一個Service方法,把@Transactional注解加到新Service方法上,把需要事務(wù)執(zhí)行的代碼移到新方法中。
自己注入自己
@Service
public class TransactionalTest implements TransactionalService{
@Resource
private TransactionalTest transactionalTest;
@Override
public void testTransactional() {
transactionalTest.transactional();
}
@Transactional
public void transactional(){}
}如果不想再新加一個Service類,在該Service類中注入自己。
完全不用擔心循環(huán)依賴的問題,spring ioc內(nèi)部的三級緩存保證了它,不會出現(xiàn)循環(huán)依賴問題。
4、事務(wù)方法中使用try-catch捕獲處理
@Transactional
public void transactional(){
try{
....
}catch(Exception e){
logger.error("",e);
}
}事務(wù)@Transactional由spring控制時,它會在拋出異常的時候進行回滾。如果自己使用try-catch捕獲處理了,是不生效的。
如果想事務(wù)生效可以進行手動回滾或者在catch里面將異常拋出【throw new RuntimeException();】
事務(wù)手動回滾
try{
...
}catch(Exception e){
log.error("fail",e);
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return false;
}回滾部分異常 使用【Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint(); 】設(shè)置回滾點。
使用【TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);】回滾到savePoint。
@Override
@Transactional(rollbackFor = Exception.class)
public Object submitOrder (){
success();
//只回滾以下異常,
Object savePoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
try {
exception();
} catch (Exception e) {
e.printStackTrace();
// 手工回滾事務(wù)
TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savePoint);
return response.error();
}
return response.success();
}到此這篇關(guān)于SpringBoot中的事務(wù)全方位詳解的文章就介紹到這了,更多相關(guān)SpringBoot中的事務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解析Spring Data JPA的Audit功能之審計數(shù)據(jù)庫變更
Spring Data JPA 提供了Audit審計功能,用來記錄創(chuàng)建時間、創(chuàng)建人、修改時間、修改人等,下面來詳細講解下審計數(shù)據(jù)庫變更2021-06-06
SpringMVC框架post提交數(shù)據(jù)庫出現(xiàn)亂碼解決方案
這篇文章主要介紹了SpringMVC框架post提交數(shù)據(jù)庫出現(xiàn)亂碼解決方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-09-09
Mybatis使用foreach批量更新數(shù)據(jù)報無效字符錯誤問題
這篇文章主要介紹了Mybatis使用foreach批量更新數(shù)據(jù)報無效字符錯誤問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08

