Spring 源碼解析 | Spring 事務(wù)
本文主要講述 Spring 事務(wù)的實現(xiàn),以及申明式事務(wù) @Transactional 使用案例。
Spring 事務(wù)
Spring Framework 為事務(wù)管理提供的事務(wù)管理器,具有以下優(yōu)點:
集成簡單,它作為 Spring Framework 的一部分。
支持申明式事務(wù)和編程式事務(wù)。
使用簡單我們只需要做對應(yīng)的配置之后,添加
@Transactional即可使用。
環(huán)境介紹:
jdk 17 、 spring 6.x
事務(wù)管理器
Spring 事務(wù)抽象的關(guān)鍵是事務(wù)策略的概念。事務(wù)策略由定義 TransactionManager,特別是 org.springframework.transaction.PlatformTransactionManager 命令式事務(wù)管理的org.springframework.transaction.ReactiveTransactionManager接口和反應(yīng)式事務(wù)管理。下面是 PlatformTransactionManager 的定義:
public interface PlatformTransactionManager extends TransactionManager {
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
復(fù)制代碼這主要是一個服務(wù)提供者接口 (SPI),盡管您可以從應(yīng)用程序代碼中以編程方式使用它 。因為 PlatformTransactionManager 是一個接口,所以可以根據(jù)需要輕松模擬或存根。它與查找策略(例如 JNDI)無關(guān)。PlatformTransactionManager 實現(xiàn)的定義與 Spring Framework IoC 容器中的任何其他對象(或 bean)一樣。即使在使用 JTA 時,僅此好處就使 Spring Framework 事務(wù)成為有價值的抽象。
同樣,按照 Spring 的理念,TransactionException可以由任何PlatformTransactionManager接口的方法拋出的是未經(jīng)檢查的(它擴(kuò)展了java.lang.RuntimeException 異常)。如果事務(wù)執(zhí)行失敗。往往在應(yīng)用程序代碼實際上可以從事務(wù)失敗中恢復(fù)的極少數(shù)情況下,我們也可以選擇捕獲 TransactionException 進(jìn)行自定義處理。
getTransaction(..) 方法 TransactionStatus 根據(jù) TransactionDefinition 參數(shù)返回一個對象 。TransactionStatus如果當(dāng)前調(diào)用堆棧中存在匹配的事務(wù),則返回的可能表示新事務(wù)或可以表示現(xiàn)有事務(wù)。后一種情況的含義是,與 Java EE 事務(wù)上下文一樣,TransactionStatus 與執(zhí)行線程相關(guān)聯(lián)(存儲到 ThreadLocal 中)。
TransactionDefinition接口提供以下定義:
傳播:通常,事務(wù)范圍內(nèi)的所有代碼都在該事務(wù)中運行。但是,如果在事務(wù)上下文已經(jīng)存在時運行事務(wù)方法,您可以指定行為。例如,代碼可以在現(xiàn)有事務(wù)中繼續(xù)運行(常見情況),或者可以暫?,F(xiàn)有事務(wù)并創(chuàng)建新事務(wù)。
隔離度:此事務(wù)與其他事務(wù)的工作隔離的程度。例如,這個事務(wù)可以看到來自其他事務(wù)的未提交的寫入。
超時:此事務(wù)在超時和被底層事務(wù)基礎(chǔ)設(shè)施自動回滾之前運行的時間。
只讀狀態(tài):當(dāng)您的代碼讀取但不修改數(shù)據(jù)時,您可以使用只讀事務(wù)。在某些情況下,只讀事務(wù)可能是一種有用的優(yōu)化,如在使用 Hibernate 的時候。
事務(wù)狀態(tài)
TransactionStatus 為事務(wù)提供了控制事務(wù)執(zhí)行和查詢事務(wù)狀態(tài)的接口, 定義如下:
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
@Override
boolean isNewTransaction();
boolean hasSavepoint();
@Override
void setRollbackOnly();
@Override
boolean isRollbackOnly();
void flush();
@Override
boolean isCompleted();
}
復(fù)制代碼使用案例
聲明式事務(wù)管理
其實 Spring 事務(wù)的核心是通過 Spring Aop 進(jìn)行介入,然后通過 TransactionManager 管理事務(wù)執(zhí)行策略,執(zhí)行過程中通過 TransactionStatus 進(jìn)行事務(wù)狀態(tài)的維護(hù)。事務(wù)代理上調(diào)用方法的概念視圖:
使用案例
下面是我一個配置 Spring Transtation 事務(wù)的一個案例, 我為了方便通過 jdbctemplate 進(jìn)行 SQL 執(zhí)行
添加依賴
implementation project(":spring-core")
implementation project(":spring-context")
implementation project(":spring-beans")
implementation project(":spring-aop")
implementation project(":spring-tx")
implementation project(":spring-jdbc")
implementation 'mysql:mysql-connector-java:5.1.34'
復(fù)制代碼添加配置
這里有 4 個配置,我們需要配置: DataSource 、DataSourceTransactionManager 、JdbcTemplate TransactionTemplate (JDBC 事務(wù)管理)
@Configuration
@Import({SummerMainService.class})
public class DataSourceConfig {
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
return jdbcTemplate;
}
/**
* JDBC 事務(wù)
*
* @param transactionManager
* @return
*/
@Bean
public TransactionTemplate transactionTemplate(DataSourceTransactionManager transactionManager) {
return new TransactionTemplate(transactionManager);
}
/**
* 申明數(shù)據(jù)源
*
* @return
*/
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://127.0.0.1/summer_test?useUnicode=true&characterEncoding=utf-8&useSSL=false");
dataSource.setUsername("root");
dataSource.setPassword("root123");
return dataSource;
}
/**
* 申明事務(wù)管理器
*
* @return
*/
@Bean
public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
復(fù)制代碼業(yè)務(wù)邏輯
下面的邏輯重要是插入一條數(shù)據(jù),如果 id % 2 == 0 就進(jìn)行事務(wù)的回滾。
public class SummerMainService {
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 編程式事務(wù) 例子: @Transactional
*/
@Transactional
public void testCommit() {
String uuid = UUID.randomUUID().toString().replace("-", "");
jdbcTemplate.update("insert into summer_main(`name`) value ('" + uuid + "')");
List程序調(diào)用
調(diào)用代碼如下,我們同樣還是分為三個步驟:創(chuàng)建容器,獲取對象,調(diào)用方法。
public class TransactionTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(DataSourceConfig.class);
SummerMainService summerMainService = applicationContext.getBean(SummerMainService.class);
summerMainService.testCommit();
}
}
復(fù)制代碼SQL 腳本
DROP TABLE IF EXISTS `summer_main`;
CREATE TABLE `summer_main` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`name` varchar(200) DEFAULT NULL COMMENT '名稱',
`remarks` varchar(200) DEFAULT NULL COMMENT '備注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO `summer_main` (`id`, `name`, `remarks`)
VALUES
(1,'1af523e120ee4229aaedea1badd6af69','WaKen Notes'),
(2,'16988516671447afa585dfc3a0069dd6',NULL),
(3,'820708fb5daa469786272f21410b53c1','WaKen Notes'),
(4,'c4247f97ed524d609e6afe2d7db461ff',NULL);
復(fù)制代碼參考資料
docs.spring.io/spring-fram…
作者:老鄭_
鏈接:https://juejin.cn/post/7018207621685444639
來源:稀土掘金
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。
