使用 Spring Transactional 註釋的最佳方式 - Vlad Mihalcea
在本文中,我將向您展示使用 Spring Transactional 註釋的最佳方式。
Spring事務註解
從 1.0 版本開始,Spring 就提供了對基於 AOP 的事務管理的支援,允許開發人員以宣告方式定義事務邊界。
不久之後,在 1.2 版本中,Spring 增加了對@Transactionalannotation的支援,這使得配置業務單位的事務邊界變得更加容易。
@Transactional註解提供了以下屬性。
- value和transactionManager - 這些屬性可以用來提供一個TransactionManager引用,以便在處理被註釋塊的事務時使用。
- 傳播 - 定義了事務邊界如何傳播到其他將被直接或間接從註釋塊中呼叫的方法。預設的傳播是REQUIRED,意味著如果還沒有事務可用,就會啟動一個事務。否則,正在進行的事務將被當前執行的方法所使用。
- timeout和timeoutString - 定義當前方法在丟擲TransactionTimedOutException之前允許執行的最大秒數。
- readOnly - 定義了當前事務是隻讀還是讀寫。
- rollbackFor和rollbackForClassName - 定義一個或多個Throwable類,當前事務將被回滾。預設情況下,如果丟擲RuntimException或Error,事務將被回滾,但如果丟擲一個檢查過的Exception,則不會被回滾。
- noRollbackFor和noRollbackForClassName - 定義一個或多個Throwable類,當前事務不會被回滾。通常情況下,你會對一個或多個RuntimException類使用這些屬性,因為你不想回滾給定的事務。
Spring Transactional註解屬於哪個層?
@Transactional註解屬於服務層,因為定義事務邊界是服務層的責任。
不要在Web層中使用它,因為這會增加資料庫事務的響應時間,並且更難為給定的資料庫事務錯誤提供正確的錯誤資訊(例如,一致性、死鎖、鎖獲取、樂觀鎖)。
DAO(資料訪問物件)或Repository層需要一個應用層的事務,但這個事務應該從服務層傳播。
使用Spring事務性註解的最佳方式
在服務層中,你可以有資料庫相關的和非資料庫相關的服務。如果一個給定的業務用例需要將它們混合在一起,比如當它必須解析一個給定的語句,建立一個報告,並將一些結果儲存到資料庫中時,如果資料庫事務儘可能晚地開始,那是最好的。
出於這個原因,你可以有一個非事務性的閘道器服務,比如下面的RevolutStatementService。
@Service public class RevolutStatementService { @Transactional(propagation = Propagation.NEVER) public TradeGainReport processRevolutStocksStatement( MultipartFile inputFile, ReportGenerationSettings reportGenerationSettings) { return processRevolutStatement( inputFile, reportGenerationSettings, stocksStatementParser ); } private TradeGainReport processRevolutStatement( MultipartFile inputFile, ReportGenerationSettings reportGenerationSettings, StatementParser statementParser ) { ReportType reportType = reportGenerationSettings.getReportType(); String statementFileName = inputFile.getOriginalFilename(); long statementFileSize = inputFile.getSize(); StatementOperationModel stocksStatementModel = statementParser.parse( inputFile, reportGenerationSettings.getFxCurrency() ); int statementChecksum = stocksStatementModel.getStatementChecksum(); TradeGainReport report = generateReport(stocksStatementModel); if(!operationService.addStatementReportOperation( statementFileName, statementFileSize, statementChecksum, reportType.toOperationType() )) { triggerInsufficientCreditsFailure(report); } return report; } } |
processRevolutStocksStatement方法是非事務性的,為此,我們可以使用Propagation.NEVER策略來確保這個方法永遠不會被活動的事務呼叫。
因此,statementParser.parse和generateReport方法是在一個非事務性的上下文中執行的,因為我們不想在只需要執行應用級處理的時候獲取一個資料庫連線並保持它。
只有operationService.addStatementReportOperation需要在事務性上下文中執行,為此,addStatementReportOperation使用了@Transactional註釋。
@Service @Transactional(readOnly = true) public class OperationService { @Transactional(isolation = Isolation.SERIALIZABLE) public boolean addStatementReportOperation( String statementFileName, long statementFileSize, int statementChecksum, OperationType reportType) { ... } } |
請注意,addStatementReportOperation覆蓋了預設的隔離級別,並指定該方法在一個SERIALIZABLE資料庫事務中執行。
另一件值得注意的事情是,該類被註解為@Transactional(readOnly = true),這意味著,預設情況下,所有服務方法都將使用這一設定,並在只讀事務中執行,除非該方法使用自己的@Trsnactional定義來覆蓋事務設定。
對於事務性服務,好的做法是在類的層面上將只讀屬性設定為 "真",並在每個需要向資料庫寫入的服務方法上覆蓋它。
例如,UserService使用同樣的模式。
@Service @Transactional(readOnly = true) public class UserService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { ... } @Transactional public void createUser(User user) { ... } } |
loadUserByUsername使用的是隻讀事務,由於我們使用的是Hibernate,Spring也執行了一些只讀的優化。
另一方面,createUser必須寫到資料庫中。因此,它用@Transactional註解給出的預設設定覆蓋了readOnly屬性值,即readOnly=false,因此使事務成為讀寫。
分割讀寫和只讀方法的另一個巨大優勢是,我們可以將它們路由到不同的資料庫節點。這樣,我們可以通過增加副本節點的數量來擴充套件只讀流量。
相關文章
- 批處理最佳實踐 - Vlad Mihalcea
- 使用JPA和Hibernate呼叫儲存過程的最佳方法 - Vlad Mihalcea儲存過程
- 使用JPA和Hibernate延遲載入實體屬性的最佳方法 - Vlad Mihalcea
- 使用DataSource-Proxy在Spring Boot中記錄SQL語句 - Vlad MihalceaSpring BootSQL
- 適合用於資料庫主鍵的最佳UUID工具庫 - Vlad Mihalcea資料庫UI
- 【Spring註解】事務註解@TransactionalSpring
- Spring @Transactional註解淺談Spring
- 使用FlexyPool度量你的XA事務連線池合適大小 - Vlad MihalceaFlex
- SQL 搜尋方法或鍵集分頁 - Vlad MihalceaSQL
- spring,mybatis事務管理配置與@Transactional註解使用[轉]SpringMyBatis
- Spring原始碼學習之:spring註解@TransactionalSpring原始碼
- 如何啟用Hibernate慢查詢日誌? -Vlad Mihalcea
- 什麼是單主資料庫複製? -Vlad Mihalcea資料庫
- JDBC驅動程式Maven依賴項大全列表 - Vlad MihalceaJDBCMaven
- 《四 spring原始碼》spring的事務註解@Transactional 原理分析Spring原始碼
- 使用Spring實現訪問主從資料庫的讀寫和只讀事務/事物的分離路由 -Vlad MihalceaSpring資料庫路由
- spring事物配置,宣告式事務管理和基於@Transactional註解的使用Spring
- JPA的事務註解@Transactional使用總結
- Spring非同步Async和事務Transactional註解Spring非同步
- 在JPA中請優先使用sequence策略生成實體識別符號的值 - Vlad Mihalcea符號
- spring boot使用註解的方式整合mybaitsSpring BootAI
- Spring宣告式事務@Transactional使用Spring
- Spring中@Transactional事務使用陷阱Spring
- Spring中@Transactional與@Async共同使用Spring
- 2PL(兩階段鎖定)演算法如何工作 -Vlad Mihalcea演算法
- 為什麼有人不推薦使用spring官方推薦的@Transactional宣告式註解Spring
- Spring 使用註解方式進行事物管理Spring
- Spring Boot Transactional註解原始碼閱讀筆記(二)Spring Boot原始碼筆記
- Spring Boot Transactional註解原始碼閱讀筆記(一)Spring Boot原始碼筆記
- Spring Boot註解@Transactional結合實際例子講解Spring Boot
- Spring IOC容器-註解的方式Spring
- 內部呼叫@Transactional 註解的方法
- 【Spring】@Transactional 閒聊Spring
- spring通過註解註冊bean的方式+spring生命週期SpringBean
- Spring 註解方式實現的AOPSpring
- spring註冊bean的幾種方式SpringBean
- Spring事務的介紹,以及基於註解@Transactional的宣告式事務Spring
- Spring(三)——註解方式(Annotation)Spring