1. Spring 源碼解析 | Spring 事務(wù)

        共 5734字,需瀏覽 12分鐘

         ·

        2021-10-16 20:16

        本文主要講述 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 個配置,我們需要配置: DataSourceDataSourceTransactionManager 、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> maps = jdbcTemplate.queryForList("select * from summer_main where `name` = '" + uuid + "'");
        Integer id = null;
        if (maps.size() > 0) {
        Map stringObjectMap = maps.get(0);
        id = Integer.parseInt(String.valueOf(stringObjectMap.get("id")));
        if (id % 2 == 0) {
        throw new RuntimeException("system error transaction rollback!");
        }
        }
        jdbcTemplate.update("update summer_main set remarks = 'WaKen Notes' where id = " + id);

        }

        }

        復(fù)制代碼

        程序調(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)載請注明出處。



        瀏覽 87
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
          
          

            1. 亚洲情色一区 | 日日干夜夜撸 | 亲子乱一区二区三区电影 | 操骚屄| 四虎精品一区 |