在現代軟體開發中,事務處理是必不可少的一部分。當多個操作需要作為一個整體來執行時,事務
可以確保資料的完整性和一致性
,並避免出現異常和錯誤情況。在SpringBoot
框架中,我們可以使用宣告式事務和程式設計式事務
來管理事務處理。其中事務的坑也是不少,比較常見的就是事務失效,大家可以看看!後面小編在出一篇事務失效場景哈,喜歡的可以關注,等待更新哈!
這篇部落格將重點探討這兩種事務處理方式的原始碼實現、區別、優缺點、適用場景以及實戰。
我們來接著說事務,裡面還涉及到三個知識點,大家可以自行百度好好了解!
本篇文章主要講的就是實現事務的兩種方式的分析!
讓我們開始探索宣告式事務和程式設計式事務吧!
文章很長,耐心看完希望對你有幫助!
本文原始碼是使用:springboot2.7.1
我們在啟動類上新增註解:@EnableTransactionManagement
後續使用就可以新增註解@Transactional(rollbackFor = Exception.class)
使用,或者是使用程式設計式事務使用了 !
後面我們在詳細演示怎麼使用哈!
public class TransactionInterceptor extends TransactionAspectSupport
implements MethodInterceptor, Serializable{}
TransactionInterceptor UML圖:
宣告式事務主要是通過AOP
實現,主要包括以下幾個節點:
啟動時掃描@Transactional
註解:在啟動時,Spring Boot會掃描所有使用了@Transactional註解的方法,並將其封裝成TransactionAnnotationParser
物件。
AOP 來實現事務管理的核心類依然是 TransactionInterceptor
。TransactionInterceptor 是一個攔截器,用於攔截使用了 @Transactional 註解的方法
將TransactionInterceptor織入到目標方法中:在AOP程式設計中,使用AspectJ
編寫切面類,通過@Around
註解將TransactionInterceptor織入到目標方法中
。
在目標方法執行前建立事務:在目標方法執行前,TransactionInterceptor會呼叫PlatformTransactionManager
建立一個新的事務,並將其納入到當前執行緒的事務上下文中。
執行目標方法:在目標方法執行時,如果發生異常,則將事務狀態標記為ROLLBACK_ONLY
;否則,將事務狀態標記為COMMIT
。
提交或回滾事務:在目標方法執行完成後,TransactionInterceptor會根據事務狀態(COMMIT或ROLLBACK_ONLY)來決定是否提交或回滾事務。
原始碼:
@Override
@Nullable
public Object invoke(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 CoroutinesInvocationCallback() {
@Override
@Nullable
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
@Override
public Object getTarget() {
return invocation.getThis();
}
@Override
public Object[] getArguments() {
return invocation.getArguments();
}
});
}
下面是核心處理方法,把不太重要的程式碼忽略了,留下每一步的節點。
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// 獲取事務屬性
final TransactionManager tm = determineTransactionManager(txAttr);
// 準備事務
TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
// 執行目標方法
Object retVal = invocation.proceedWithInvocation();
// 回滾事務
completeTransactionAfterThrowing(txInfo, ex);
// 提交事務
commitTransactionAfterReturning(txInfo);
}
程式設計式事務主要下面的程式碼:
public class TransactionTemplate extends DefaultTransactionDefinition
implements TransactionOperations, InitializingBean{}
TransactionTemplate UML圖:
TransactionTemplate
類的execute()
方法封裝了事務的具體實現,通過呼叫TransactionCallback物件的doInTransaction()
方法來執行業務邏輯並管理事務
。在具體實現中,TransactionTemplate類會自動控制事務的提交和回滾,並將異常丟擲給上層呼叫者進行處理。
@Override
@Nullable
public <T> T execute(TransactionCallback<T> action) throws TransactionException {
Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
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 | Error ex) {
// Transactional code threw application exception -> rollback
rollbackOnException(status, ex);
throw ex;
}
catch (Throwable 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;
}
}
上面說了原始碼裡的大體實現,下面我們來介紹一下兩者區別:
總體而言,宣告式事務和程式設計式事務都有各自的優缺點,開發人員需要根據具體需求選擇適合的方式來控制事務。
補充:
大事務時間過長可能會導致以下問題:
資料庫鎖定
:當事務涉及到大量的資料操作時,事務可能會佔用資料庫資源並長時間鎖定相關資料。這可能會導致其他事務無法存取或修改這些資料,從而降低系統的並行效能和吞吐量。
資源耗盡
:長時間執行的事務需要佔用更多的系統資源,如記憶體和CPU等。如果系統資源不足,可能會導致系統出現延遲、死鎖等問題,甚至導致系統崩潰。
事務失敗概率增加
:當事務時間過長時,事務執行期間可能會發生各種錯誤,如網路故障、硬體故障、作業系統問題等。此時,事務可能無法成功提交,導致資料丟失或資料不一致。
應用程式超時
:應用程式通常會為每個事務設定一個超時時間,以避免事務持續時間過長。如果事務持續時間超過設定的超時時間,則應用程式可能會因為等待事務完成而阻塞,最終導致應用程式崩潰或超時。
回滾時間增加
:如果事務失敗需要回滾,長時間執行的事務將需要更長的時間來進行回滾操作。這可能會導致資料不一致或丟失,並增加資料庫維護的工作量。
因此,開發人員應該儘量避免事務時間過長,合理地設定事務範圍、優化事務操作方式以及減少資料存取次數等措施,以提高系統的並行效能和吞吐量。
方案:
大事務可以拆分小的事務,一下查詢方面的可以提取出來,運算元據庫的抽離出來專門加上事務。
也可以使用CompletableFuture
組合式非同步編排來解決大事務的問題!!
宣告式事務通常通過AOP
技術實現,在方法或類級別上宣告事務屬性。
宣告式事務的優點包括:
簡化程式碼
:開發人員只需要關注業務邏輯,而無需手動管理事務,可以減少程式碼複雜度和工作量。
可設定性強
:事務屬性可以通過XML檔案、註解等方式進行設定,靈活方便。
易於擴充套件
:可以通過AOP技術輕鬆地擴充套件使其支援新的事務策略。
宣告式事務存在以下缺點:
限制較大
:事務屬性需要在方法或類級別進行宣告,這可能會導致某些情況下難以滿足特定的業務需求。
難以偵錯
:由於事務是在AOP層面進行管理的,因此在偵錯時可能難以追蹤事務管理的具體細節。
程式設計式事務通常通過API
介面實現,開發人員可以在程式碼中顯式地管理事務。
程式設計式事務的優點包括:
靈活性強
:開發人員可以在程式碼中根據具體業務需要來控制事務的具體範圍和屬性。
易於偵錯
:由於事務管理在程式碼層面上實現,因此開發人員可以很容易地追蹤事務管理的細節。
程式設計式事務存在以下缺點:
程式碼複雜度高
:需要在程式碼中手動處理事務,並處理各種異常情況,可能會增加程式碼的複雜度和工作量。
可設定性差
:事務的範圍和屬性需要在程式碼中顯式宣告,這可能會導致一些特定的業務需求難以滿足。
總之,宣告式事務和程式設計式事務各有優缺點。開發人員需要根據具體業務需求和場景選擇使用合適的事務管理方式。
宣告式事務通常適用於以下場景:
而程式設計式事務通常適用於以下場景:
在實際場景中,可以根據需求綜合考慮使用宣告式事務和程式設計式事務的優勢來進行選擇。
根據不同的使用者量來具體選擇,在幾乎沒有並行量的系統設計一條非同步編排反而大材小用,可能造成資源的浪費;但是有需要等待遠端API的響應時
,使用非同步編排可以將等待時間最小化
,並使得應用程式不必阻塞等待API響應,從而提高使用者體驗。
很多事情沒有絕對化,只有相對化,只要能支援現有正常的使用,不管什麼樣的設計都是沒問題的!
可能好的設計會使系統在經受並行量增大的過程中無感,還是要調研清楚,從而設計出更好的方案,防止資源浪費!
儘管小編還沒有什麼架構經驗,但還是對架構充滿興趣,不想做架構師的開發不是好開發哈!!當然你也可以走管理!!
這裡就簡單模擬一下,為了模擬報錯,把OperIp設定為唯一!
@Transactional(rollbackFor = Exception.class)
大家經常使用,就不多演示了!
@Transactional(rollbackFor = Exception.class)
@Override
public void template() {
SysLog sysLog = new SysLog();
sysLog.setOperIp("123");
SysLog sysLog1 = new SysLog();
sysLog1.setOperIp("hhh");
log.info("插入第一條資料開始========");
testMapper.insert(sysLog);
log.info("插入第一條資料完成========");
log.info("插入第二條資料開始========");
testMapper.insert(sysLog);
log.info("插入第二條資料完成========");
}
此時資料沒有資料,全部回滾成功!
首先注入TransactionTemplate
:
@Autowired
private TransactionTemplate transactionTemplate;
後面直接使用即可:
@Override
public void template() {
SysLog sysLog = new SysLog();
sysLog.setOperIp("123");
SysLog sysLog1 = new SysLog();
sysLog1.setOperIp("hhh");
log.info("插入第一條資料開始========");
testMapper.insert(sysLog);
log.info("插入第一條資料完成========");
transactionTemplate.execute(status -> {
log.info("程式設計式事務中:插入第一條資料開始========");
testMapper.insert(sysLog1);
log.info("程式設計式事務中:插入第一條資料完成========");
log.info("程式設計式事務中:插入第二條資料開始========");
int insert = testMapper.insert(sysLog);
log.info("程式設計式事務中:插入第二條資料完成========");
return insert;
});
}
檢視資料庫,第一條不在程式設計式事務內不會參與回滾!
本文介紹了SpringBoot框架中的宣告式事務和程式設計式事務,並分析了它們的原始碼實現、區別、優缺點、適用場景以及實戰。
無論是採用哪種方式來管理事務,都需要考慮到業務需求和開發團隊的實際情況,選擇合適的事務處理方式,以確保系統的可靠性和穩定性。
希望通過本文的介紹,你能夠更好地理解宣告式事務和程式設計式事務的概念和原理,在開發過程中選擇合適的事務處理方式,提高專案的可維護性和穩定性。
如果對你有幫助,還請動一下您的發財小手,關注一下公眾號哈!!謝謝您的關注!!文章首發看!!!
建了一個IT交流群,歡迎大家加入,過期加我拉你們進哈!