最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring事務(wù)和事務(wù)傳播機(jī)制操作大全

 更新時(shí)間:2025年12月17日 15:43:18   作者:努力也學(xué)不會(huì)Java.  
Spring中事務(wù)管理主要分為編程式事務(wù)和聲明式事務(wù),聲明式事務(wù)推薦使用,聲明式事務(wù)通過`@Transactional`注解實(shí)現(xiàn),可以簡化事務(wù)操作,本文給大家介紹Spring事務(wù)和事務(wù)傳播機(jī)制,感興趣的朋友跟隨小編一起看看吧

一、事務(wù)

1.1、什么是事務(wù)

事務(wù)是一組操作的集合, 是一個(gè)不可分割的操作.

事務(wù)會(huì)把所有的操作作為一個(gè)整體, 一起向數(shù)據(jù)庫提交或者是撤銷操作請求. 所以這組操作要么同時(shí)成功,要么同時(shí)失敗。

1.2、為什么需要事務(wù)

我們在進(jìn)行程序開發(fā)時(shí), 也會(huì)有事務(wù)的需求.

比如轉(zhuǎn)賬操作: 第一步:A 賬戶 -100 元. 第二步:B 賬戶 +100 元. 如果沒有事務(wù),第?步執(zhí)行成功了, 第?步執(zhí)行失敗了, 那么A 賬戶的100 元就平白無故消失了. 如果使 用事務(wù)就可以解決這個(gè)問題, 讓這?組操作要么?起成功, 要么一起失敗.

比如秒殺系統(tǒng),
第一步: 下單成功
第二步: 扣減庫存

下單成功后, 庫存也需要同步減少. 如果下單成功, 庫存扣減失敗, 那么就會(huì)造成下單超出的情況. 所以就需要把這兩步操作放在同一個(gè)事務(wù)中. 要么一起成功, 要么一起失敗。

1.3、事務(wù)的操作

事務(wù)的操作主要有三步:

1. 開啟事start transaction/ begin (?組操作前開啟事務(wù))
2. 提交事務(wù): commit (這組操作全部成功, 提交事務(wù))
3. 回滾事務(wù): rollback (這組操作中間任何?個(gè)操作出現(xiàn)異常, 回滾事務(wù))

-- 開啟事務(wù)
start transaction;
-- 提交事務(wù)
commit;
-- 回滾事務(wù)
rollback;

二、Spring中事務(wù)的實(shí)現(xiàn)

Spring 中的事務(wù)操作分為兩類:

  1. 編程式事務(wù)(手動(dòng)寫代碼操作事務(wù)).
  2. 聲明式事務(wù)(利用注解自動(dòng)開啟和提交事務(wù)).

在了解事務(wù)之前,我們先準(zhǔn)備數(shù)據(jù)和數(shù)據(jù)的訪問代碼:

需求: 用戶注冊, 注冊時(shí)在日志表中插入?條操作記錄:

數(shù)據(jù)準(zhǔn)備:

-- 創(chuàng)建數(shù)據(jù)庫
DROP DATABASE IF EXISTS trans_test;
CREATE DATABASE trans_test DEFAULT CHARACTER SET utf8mb4;
-- ??表
DROP TABLE IF EXISTS user_info;
CREATE TABLE user_info (
	 `id` INT NOT NULL AUTO_INCREMENT,
	 `user_name` VARCHAR (128) NOT NULL,
	 `password` VARCHAR (128) NOT NULL,
	 `create_time` DATETIME DEFAULT now(),
	 `update_time` DATETIME DEFAULT now() ON UPDATE now(),
	 PRIMARY KEY (`id`)
) ENGINE = INNODB DEFAULT CHARACTER
SET = utf8mb4 COMMENT = '用戶表';
-- 操作?志表
DROP TABLE IF EXISTS log_info;
CREATE TABLE log_info (
	 `id` INT PRIMARY KEY auto_increment,
 	`user_name` VARCHAR ( 128 ) NOT NULL,
	 `op` VARCHAR ( 256 ) NOT NULL,
	 `create_time` DATETIME DEFAULT now(),
	 `update_time` DATETIME DEFAULT now() ON UPDATE now() 
) DEFAULT charset 'utf8mb4';

代碼準(zhǔn)備:
1.創(chuàng)建項(xiàng)目 spring-trans, 引?Spring Web, Mybatis, mysql等依賴

2 . 配置文件:

spring:
 datasource:
 url: jdbc:mysql://127.0.0.1:3306/trans_testcharacterEncoding=utf8&useSSL=false
 username: root
 password: root
 driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
 configuration: # 配置打印 MyBatis?志
  log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  map-underscore-to-camel-case: true #配置駝峰?動(dòng)轉(zhuǎn)換

3.實(shí)體類

  @Data
  public class UserInfo {
        private Integer id;
        private String userName;
        private String password;
        private Date createTime;
        private Date updateTime;
    }
@Data
public class LogInfo {
	 private Integer id;
	 private String userName;
	 private String op;
	 private Date createTime;
	 private Date updateTime;
}

4.Mapper

 @Mapper
    public interface UserInfoMapper {
        @Insert("insert into user_info(`user_name`,`password`)values(#{name},#
                {password})")
        Integer insert(String name, String password);
    }
    @Mapper
    public interface LogInfoMapper {
        @Insert("insert into log_info(`user_name`,`op`)values(#{name},#{op})")
        Integer insertLog(String name, String op);
    }

5.Service

@Slf4j
    @Service
    public class UserService {
        @Autowired
        private UserInfoMapper userInfoMapper;
        public void registryUser(String name, String password) {
            //插???信息
            userInfoMapper.insert(name, password);
        }
    }
    @Slf4j
    @Service
    public class LogService {
        @Autowired
        private LogInfoMapper logInfoMapper;
        public void insertLog(String name, String op) {
            //記錄??操作
            logInfoMapper.insertLog(name, "?戶注冊");
        }
    }

6.Controller

@RequestMapping("/user")
    @RestController
    public class UserController {
        @Autowired
        private UserService userService;
        @RequestMapping("/registry")
        public String registry(String name, String password) {
            //??注冊
            userService.registryUser(name, password);
            return "注冊成功";
        }
    }

2.1、Spring編程式事務(wù)

Spring 手動(dòng)操作事務(wù)和上面 MySQL 操作事務(wù)類似, 有 3 個(gè)重要操作步驟:
• 開啟事務(wù)(獲取事務(wù))
• 提交事務(wù)
• 回滾事務(wù)

SpringBoot 內(nèi)置了兩個(gè)對象:

  1. DataSourceTransactionManager 事務(wù)管理器. 用來獲取事務(wù)(開啟事務(wù)), 提交或回滾事務(wù)的
  2. TransactionDefinition 是事務(wù)的屬性, 在獲取事務(wù)的時(shí)候需要將TransactionDefinition 傳遞進(jìn)去從?獲得?個(gè)事務(wù) TransactionStatus。

我們還是根據(jù)代碼的實(shí)現(xiàn)來學(xué)習(xí):

@RequestMapping("/user")
    @RestController
    public class UserController {
        // JDBC 事務(wù)管理器
        @Autowired
        private DataSourceTransactionManager dataSourceTransactionManager;
        // 定義事務(wù)屬性
        @Autowired
        private TransactionDefinition transactionDefinition;
        @Autowired
        private UserService userService;
        @RequestMapping("/registry")
        public String registry(String name, String password) {
            // 開啟事務(wù)
            TransactionStatus transactionStatus = dataSourceTransactionManager
                    .getTransaction(transactionDefinition);
            //??注冊
            userService.registryUser(name, password);
            //提交事務(wù)
            dataSourceTransactionManager.commit(transactionStatus);
            //回滾事務(wù)
            //dataSourceTransactionManager.rollback(transactionStatus);
            return "注冊成功";
        }
    }

運(yùn)?程序之后,用postman測試接口,觀看數(shù)據(jù)庫結(jié)果數(shù)據(jù)插入成功。

觀察事務(wù)回滾:

  //回滾事務(wù) 
  dataSourceTransactionManager.rollback(transactionStatus);

觀察數(shù)據(jù)庫, 雖然程序返回"注冊成功", 但數(shù)據(jù)庫并沒有新增數(shù)據(jù).

以上代碼雖然可以實(shí)現(xiàn)事務(wù), 但操作也很繁瑣, 有沒有更簡單的實(shí)現(xiàn)?法呢? 接下來我們學(xué)習(xí)聲明式事務(wù)。

2.2、Spring 聲明式事務(wù) @Transactional

聲明式事務(wù)的實(shí)現(xiàn)很簡單, 只需要在需要事務(wù)的放法上添加 @Transactional 注解就可以實(shí)現(xiàn)了. 無需手動(dòng)開啟事務(wù)和提交事務(wù), 進(jìn)入方法時(shí)自動(dòng)開啟事務(wù), 方法執(zhí)行完會(huì)自動(dòng)提交事務(wù), 如果中途發(fā)生了 沒有處理的異常會(huì)自動(dòng)回滾事務(wù).

我們來看代碼實(shí)現(xiàn):

    @RequestMapping("/trans")
    @RestController
    public class TransactionalController {
        @Autowired
        private UserService userService;
        @Transactional
        @RequestMapping("/registry")
        public String registry(String name, String password) {
            //??注冊
            userService.registryUser(name, password);
            return "注冊成功";
        }
    }

運(yùn)?程序, 發(fā)現(xiàn)數(shù)據(jù)插?成功.

修改程序, 使之出現(xiàn)異常

    @Slf4j
    @RequestMapping("/trans")
    @RestController
    public class TransactionalController {
        @Autowired
        private UserService userService;
        @Transactional
        @RequestMapping("/registry")
        public String registry(String name, String password) {
            //??注冊
            userService.registryUser(name, password);
            log.info("??數(shù)據(jù)插?成功");
            //強(qiáng)制程序拋出異常
            int a = 10 / 0;
            return "注冊成功";
        }
    }

運(yùn)行程序: 發(fā)現(xiàn)雖然日志顯示數(shù)據(jù)插?成功, 但數(shù)據(jù)庫卻沒有新增數(shù)據(jù), 事務(wù)進(jìn)行了回滾.

@Transactional 作?
@Transactional 可以用來修飾方法或類:
• 修飾方法時(shí): 只有修飾public方法時(shí)才生效(修飾其他方法時(shí)不會(huì)報(bào)錯(cuò), 也不生效)
• 修飾類時(shí): 對 @Transactional 修飾的類中所有的 public 方法都生效

方法/類被 @Transactional 注解修飾時(shí), 在目標(biāo)方法執(zhí)行開始之前, 會(huì)?動(dòng)開啟事務(wù), ?法執(zhí)行結(jié)束 之后, 自動(dòng)提交事務(wù).
如果在方法執(zhí)行過程中, 出現(xiàn)異常, 且異常未被捕獲, 就進(jìn)行事務(wù)回滾操作.
如果異常被程序捕獲, 方法就被認(rèn)為是成功執(zhí)行, 依然會(huì)提交事務(wù).

修改上述代碼, 對異常進(jìn)?捕獲:

@Transactional
    @RequestMapping("/registry")
    public String registry(String name, String password) {
        //??注冊
        userService.registryUser(name, password);
        log.info("??數(shù)據(jù)插?成功");
        //對異常進(jìn)?捕獲
        try {
            //強(qiáng)制程序拋出異常
            int a = 10 / 0;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "注冊成功";
    }

運(yùn)?程序, 發(fā)現(xiàn)雖然程序出錯(cuò)了, 但是由于異常被捕獲了, 所以事務(wù)依然得到了提交. 如果需要事務(wù)進(jìn)?回滾, 有以下兩種方式:

  1. 重新拋出異常
@Transactional
    @RequestMapping("/registry")
    public String registry(String name, String password) {
        //??注冊
        userService.registryUser(name, password);
        log.info("??數(shù)據(jù)插?成功");
        //對異常進(jìn)?捕獲
        try {
            //強(qiáng)制程序拋出異常
            int a = 10 / 0;
        } catch (Exception e) {
            //將異常重新拋出去
            throw e;
        }
        return "注冊成功";
    }
  1. 手動(dòng)回滾事務(wù)

使用 TransactionAspectSupport.currentTransactionStatus() 得到當(dāng)前的事務(wù), 并 使? setRollbackOnly 設(shè)置 setRollbackOnly。

 	@Transactional
    @RequestMapping("/registry")
    public String registry(String name, String password) {
        //??注冊
        userService.registryUser(name, password);
        log.info("??數(shù)據(jù)插?成功");
        //對異常進(jìn)?捕獲
        try {
            //強(qiáng)制程序拋出異常
            int a = 10 / 0;
        } catch (Exception e) {
            // ?動(dòng)回滾事務(wù)
   	TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return "注冊成功";
    }

三、@Transactional 詳解

通過上面的代碼, 我們學(xué)習(xí)了 @Transactional 的基本使用. 接下來我們學(xué)習(xí) @Transactional
注解的使用細(xì)節(jié). 我們主要學(xué)習(xí) @Transactional 注解當(dāng)中的三個(gè)常見屬性:

  1. rollbackFor: 異?;貪L屬性. 指定能夠觸發(fā)事務(wù)回滾的異常類型. 可以指定多個(gè)異常類型
  2. Isolation: 事務(wù)的隔離級(jí)別. 默認(rèn)值為 Isolation.DEFAULT
  3. propagation: 事務(wù)的傳播機(jī)制. 默認(rèn)值為 Propagation.REQUIRED

3.1、rollbackFor

@Transactional 默認(rèn)只在遇到運(yùn)行時(shí)異常和Error時(shí)才會(huì)回滾, 非運(yùn)行時(shí)異常不回滾. 即Exception的子類中, 除了RuntimeException及其子類。

如果我們需要所有異常都回滾, 需要來配置 @Transactional 注解當(dāng)中的 rollbackFor 屬性, 通過 rollbackFor 這個(gè)屬性指定出現(xiàn)何種異常類型時(shí)事務(wù)進(jìn)行回滾。

    @Transactional(rollbackFor = Exception.class)
    @RequestMapping("/r2")
    public String r2(String name, String password) throws IOException {
        //??注冊
        userService.registryUser(name, password);
        log.info("??數(shù)據(jù)插?成功");
        if (true) {
            throw new IOException();
        }
        return "r2";
    }

運(yùn)行程序后,發(fā)現(xiàn)雖然程序拋出了異常, 但是事務(wù)依然進(jìn)?了提交.

結(jié)論:
• 在Spring的事務(wù)管理中,默認(rèn)只在遇到運(yùn)行時(shí)異常RuntimeException和Error時(shí)才會(huì)回滾.
• 如果需要回滾指定類型的異常, 可以通過rollbackFor屬性來指定.

3.2事務(wù)隔離級(jí)別

3.2.1、MySQL 事務(wù)隔離級(jí)別

SQL 標(biāo)準(zhǔn)定義了四種隔離級(jí)別, MySQL 全都?持. 這四種隔離級(jí)別分別是:

  1. 讀未提交(READ UNCOMMITTED): 讀未提交, 也叫未提交讀. 該隔離級(jí)別的事務(wù)可以看到其他事務(wù)中 未提交的數(shù)據(jù). 因?yàn)槠渌聞?wù)未提交的數(shù)據(jù)可能會(huì)發(fā)生回滾, 但是該隔離級(jí)別卻可以讀到, 我們把該級(jí)別讀到的數(shù) 據(jù)稱之為臟數(shù)據(jù), 這個(gè)問題稱之為臟讀.
    2 . 讀提交(READ COMMITTED): 讀已提交, 也叫提交讀. 該隔離級(jí)別的事務(wù)能讀取到已經(jīng)提交事務(wù)的數(shù) 據(jù),該隔離級(jí)別不會(huì)有臟讀的問題.但由于在事務(wù)的執(zhí)?中可以讀取到其他事務(wù)提交的結(jié)果, 所以在不 同時(shí)間的相同 SQL 查詢可能會(huì)得到不同的結(jié)果, 這種現(xiàn)象叫做不可重復(fù)讀
    3 . 可重復(fù)讀(REPEATABLE READ): 事務(wù)不會(huì)讀到其他事務(wù)對已有數(shù)據(jù)的修改, 即使其他事務(wù)已提交. 也 就可以確保同?事務(wù)多次查詢的結(jié)果?致, 但是其他事務(wù)新插?的數(shù)據(jù), 是可以感知到的. 這也就引 發(fā)了幻讀問題. 可重復(fù)讀, 是 MySQL 的默認(rèn)事務(wù)隔離級(jí)別. 比如此級(jí)別的事務(wù)正在執(zhí)行時(shí), 另?個(gè)事務(wù)成功的插?了某條數(shù)據(jù), 但因?yàn)樗看尾樵兊慕Y(jié)果都是?樣的, 所以會(huì)導(dǎo)致查詢不到這條數(shù)據(jù), 自己重復(fù)插入時(shí)又失敗(因?yàn)槲?約束的原因). 明明在事務(wù)中查詢不到這條信息,但自己就是插入不進(jìn)去, 這個(gè)現(xiàn)象叫幻讀.
    4 . 串行化(SERIALIZABLE): 序列化, 事務(wù)最高隔離級(jí)別. 它會(huì)強(qiáng)制事務(wù)排序, 使之不會(huì)發(fā)生沖突, 從而解決了臟讀, 不可重復(fù)讀和幻讀問題, 但因?yàn)閳?zhí)行效率低, 所以真正使用的場景并不多.

3.2.2、Spring 事務(wù)隔離級(jí)別

Spring 中事務(wù)隔離級(jí)別有5 種:

  1. Isolation.DEFAULT : 以連接的數(shù)據(jù)庫的事務(wù)隔離級(jí)別為主.
    2 . Isolation.READ_UNCOMMITTED : 讀未提交, 對應(yīng)SQL標(biāo)準(zhǔn)中 READ UNCOMMITTED
    3 . Isolation.READ_COMMITTED : 讀已提交,對應(yīng)SQL標(biāo)準(zhǔn)中 READ COMMITTED
    4 . Isolation.REPEATABLE_READ : 可重復(fù)讀, 對應(yīng)SQL標(biāo)準(zhǔn)中 REPEATABLE READ
    5 . Isolation.SERIALIZABLE : 串?化, 對應(yīng)SQL標(biāo)準(zhǔn)中 SERIALIZABLE
 public enum Isolation {
        DEFAULT(-1),
        READ_UNCOMMITTED(1),
        READ_COMMITTED(2),
        REPEATABLE_READ(4),
        SERIALIZABLE(8);
        private final int value;

        private Isolation(int value) {
            this.value = value;
        }

        public int value() {
            return this.value;
        }
    }

Spring 中事務(wù)隔離級(jí)別可以通過 @Transactional 中的 isolation 屬性進(jìn)行設(shè)置。

@Transactional(isolation = Isolation.READ_COMMITTED)
@RequestMapping("/r3")
public String r3(String name,String password) throws IOException {
 	//... 代碼省略
 	return "r3"; 
}

3.3、Spring 事務(wù)傳播機(jī)制

3.3.1、什么是事務(wù)傳播機(jī)制?

事務(wù)傳播機(jī)制就是: 多個(gè)事務(wù)方法存在調(diào)用關(guān)系時(shí), 事務(wù)是如何在這些方法間進(jìn)行傳播的?

比如有兩個(gè)方法A, B都被 @Transactional 修飾, A方法調(diào)用B方法 A方法運(yùn)行時(shí), 會(huì)開啟?個(gè)事務(wù). 當(dāng)A調(diào)用B時(shí), B方法本身也有事務(wù), 此時(shí)B方法運(yùn)行時(shí), 是加入A的事務(wù), 還 是創(chuàng)建一個(gè)新的事務(wù)呢? 這個(gè)就涉及到了事務(wù)的傳播機(jī)制。

比如公司流程管理
執(zhí)行任務(wù)之前, 需要先寫執(zhí)行?檔, 任務(wù)執(zhí)行結(jié)束, 再寫總結(jié)匯報(bào)

此時(shí)A部門有?項(xiàng)工作, 需要B部門的支援, 此時(shí)B部門是直接使用A部門的?檔, 還是新建一個(gè)文檔呢?

事務(wù)隔離級(jí)別解決的是多個(gè)事務(wù)同時(shí)調(diào)用一個(gè)數(shù)據(jù)庫的問題:

而事務(wù)傳播機(jī)制解決的是?個(gè)事務(wù)在多個(gè)節(jié)點(diǎn)(?法)中傳遞的問題:

3.3.2、事務(wù)的傳播機(jī)制有哪些

@Transactional 注解支持事務(wù)傳播機(jī)制的設(shè)置, 通過 propagation 屬性來指定傳播?為.Spring 事務(wù)傳播機(jī)制有以下 7 種:

  1. Propagation.REQUIRED : 默認(rèn)的事務(wù)傳播級(jí)別. 如果當(dāng)前存在事務(wù), 則加入該事務(wù). 如果當(dāng)前沒有事務(wù), 則創(chuàng)建一個(gè)新的事務(wù).
    2 . Propagation.SUPPORTS : 如果當(dāng)前存在事務(wù), 則加入該事務(wù). 如果當(dāng)前沒有事務(wù), 則以非事務(wù)的方式繼續(xù)運(yùn)行.
    3 . Propagation.MANDATORY :強(qiáng)制性. 如果當(dāng)前存在事務(wù), 則加?該事務(wù). 如果當(dāng)前沒有事務(wù), 則 拋出異常.
    4 . Propagation.REQUIRES_NEW : 創(chuàng)建?個(gè)新的事務(wù). 如果當(dāng)前存在事務(wù), 則把當(dāng)前事務(wù)掛起. 也 就是說不管外部?法是否開啟事務(wù), Propagation.REQUIRES_NEW 修飾的內(nèi)部方法都會(huì)新開 啟??的事務(wù), 且開啟的事務(wù)相互獨(dú)立, 互不干擾.
    5 . Propagation.NOT_SUPPORTED : 以非事務(wù)方式運(yùn)行, 如果當(dāng)前存在事務(wù), 則把當(dāng)前事務(wù)掛起(不用).
    6 . Propagation.NEVER : 以非事務(wù)方式運(yùn)行, 如果當(dāng)前存在事務(wù), 則拋出異常.
    7 . Propagation.NESTED : 如果當(dāng)前存在事務(wù), 則創(chuàng)建?個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運(yùn)行. 如果當(dāng)前沒有事務(wù), 則該取值等價(jià)于 PROPAGATION_REQUIRED .
 public enum Propagation {
        REQUIRED(0),
        SUPPORTS(1),
        MANDATORY(2),
        REQUIRES_NEW(3),
        NOT_SUPPORTED(4),
        NEVER(5),
        NESTED(6);
        private final int value;
        private Propagation(int value) {
            this.value = value;
        }
        public int value() {
            return this.value;
        }
    }

3.3.3、Spring 事務(wù)傳播機(jī)制使用和各種場景演示

對于以上事務(wù)傳播機(jī)制,我們重點(diǎn)關(guān)注以下兩個(gè)就可以了:

  1. REQUIRED(默認(rèn)值)
  2. REQUIRES_NEW
3.3.3.1 REQUIRED(加入事務(wù))

看下?代碼實(shí)現(xiàn):

  1. 用戶注冊, 插入一條數(shù)據(jù)
  2. 記錄操作日志, 插入一條數(shù)據(jù)(出現(xiàn)異常)
    觀察 propagation = Propagation.REQUIRED 的執(zhí)行結(jié)果:
@RequestMapping("/propaga")
    @RestController
    public class PropagationController {
        @Autowired
        private UserService userService;
        @Autowired
        private LogService logService;
        @Transactional(propagation = Propagation.REQUIRED)
        @RequestMapping("/p1")
        public String r3(String name, String password) {
            //??注冊
            userService.registryUser(name, password);
            //記錄操作?志
            logService.insertLog(name, "??注冊");
            return "r3";
        }
    }

對應(yīng)的UserService和LogService都添加上 @Transactional(propagation =
Propagation.REQUIRED)。

@Slf4j
    @Service
    public class UserService {
        @Autowired
        private UserInfoMapper userInfoMapper;
        @Transactional(propagation = Propagation.REQUIRED)
        public void registryUser(String name, String password) {
            //插???信息
            userInfoMapper.insert(name, password);
        }
    }
    @Slf4j
    @Service
    public class LogService {
        @Autowired
        private LogInfoMapper logInfoMapper;
        @Transactional(propagation = Propagation.REQUIRED)
        public void insertLog(String name, String op) {
            int a = 10 / 0;
            //記錄??操作
            logInfoMapper.insertLog(name, "??注冊");
        }
    }

運(yùn)行程序, 發(fā)現(xiàn)數(shù)據(jù)庫沒有插入任何數(shù)據(jù). 流程描述:

  1. p1 方法開始事務(wù)
  2. 用戶注冊, 插入一條數(shù)據(jù) (執(zhí)行成功) (和p1 使用同?個(gè)事務(wù))
  3. 記錄操作日志, 插入一條數(shù)據(jù)(出現(xiàn)異常, 執(zhí)行失敗) (和p1 使用同?個(gè)事務(wù))
  4. 因?yàn)椴襟E3出現(xiàn)異常, 事務(wù)回滾. 步驟2和3使用同?個(gè)事務(wù), 所以步驟2的數(shù)據(jù)也回滾了.
3.3.3.2、REQUIRES_NEW(新建事務(wù))

將上述UserService 和LogService 中相關(guān)?法事務(wù)傳播機(jī)制改為Propagation.REQUIRES_NEW。

 @Service
    public class UserService {
        @Autowired
        private UserInfoMapper userInfoMapper;
        @Transactional(propagation = Propagation.REQUIRES_NEW)
        public void registryUser(String name, String password) {
            //插???信息
            userInfoMapper.insert(name, password);
        }
    }
    @Service
    public class LogService {
        @Autowired
        private LogInfoMapper logInfoMapper;
        @Transactional(propagation = Propagation.REQUIRES_NEW)
        public void insertLog(String name, String op) {
            int a = 10 / 0;
            //記錄??操作
            logInfoMapper.insertLog(name, "??注冊");
        }
    }

運(yùn)行程序, 發(fā)現(xiàn)用戶數(shù)據(jù)插入成功了, 日志表數(shù)據(jù)插入失敗.
LogService 方法中的事務(wù)不影響 UserService 中的事務(wù).

當(dāng)我們不希望事務(wù)之間相互影響時(shí), 可以使用該傳播行為.

3.3.3、NEVER (不支持當(dāng)前事務(wù), 拋異常)

修改UserService 中對應(yīng)?法的事務(wù)傳播機(jī)制為 Propagation.NEVER

    @Slf4j
    @Service
    public class UserService {
        @Autowired
        private UserInfoMapper userInfoMapper;
        @Transactional(propagation = Propagation.NEVER)
        public void registryUser(String name, String password) {
            //插???信息
            userInfoMapper.insert(name, password);
        }
    }

程序執(zhí)行報(bào)錯(cuò), 沒有數(shù)據(jù)插入.

3.3.3.4、NESTED(嵌套事務(wù))

將上述UserService 和LogService 中相關(guān)方法事務(wù)傳播機(jī)制改為 Propagation.NESTED。

@Slf4j
    @Service
    public class UserService {
        @Autowired
        private UserInfoMapper userInfoMapper;
        @Transactional(propagation = Propagation.NESTED)
        public void registryUser(String name, String password) {
            //插???信息
            userInfoMapper.insert(name, password);
        }
    }
    @Slf4j
    @Service
    public class LogService {
        @Autowired
        private LogInfoMapper logInfoMapper;
        @Transactional(propagation = Propagation.NESTED)
        public void insertLog(String name, String op) {
            int a = 10 / 0;
            //記錄??操作
            logInfoMapper.insertLog(name, "??注冊");
        }
    }

運(yùn)?程序, 發(fā)現(xiàn)沒有任何數(shù)據(jù)插入. 流程描述:

  1. Controller 中p1 方法開始事務(wù)
  2. UserService 用戶注冊, 插入一條數(shù)據(jù) (嵌套p1事務(wù))
  3. LogService 記錄操作日志, 插入一條數(shù)據(jù)(出現(xiàn)異常, 執(zhí)行失敗) (嵌套p1事務(wù), 回滾當(dāng)前事務(wù), 數(shù)據(jù)添加失敗)
  4. 由于是嵌套事務(wù), LogService 出現(xiàn)異常之后, 往上找調(diào)用它的方法和事務(wù), 所以用戶注冊也失敗了.
  5. 最終結(jié)果是兩個(gè)數(shù)據(jù)都沒有添加
    p1事務(wù)可以認(rèn)為是父事務(wù), 嵌套事務(wù)是子事務(wù). 父事務(wù)出現(xiàn)異常, 子事務(wù)也會(huì)回滾, 子事務(wù)出現(xiàn)異常, 如果不進(jìn)行處理, 也會(huì)導(dǎo)致父事務(wù)回滾.
3.3.3.5 NESTED和REQUIRED 有什么區(qū)別?

我們在 LogService 進(jìn)?當(dāng)前事務(wù)回滾, 修改 LogService 代碼如下:

 @Service
    public class LogService {
        @Autowired
        private LogInfoMapper logInfoMapper;
        @Transactional(propagation = Propagation.NESTED)
        public void insertLog(String name, String op) {
            try {
                int a = 10 / 0;
            } catch (Exception e) {
                //回滾當(dāng)前事務(wù)
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            }
            //記錄??操作
            logInfoMapper.insertLog(name, "??注冊");
        }
    }

重新運(yùn)行程序, 發(fā)現(xiàn)用戶表數(shù)據(jù)添加成功, 日志表添加失敗.
LogService 中的事務(wù)已經(jīng)回滾, 但是嵌套事務(wù)不會(huì)回滾嵌套之前的事務(wù), 也就是說嵌套事務(wù)可以實(shí)現(xiàn)部分事務(wù)回滾。

對?REQUIRED
把 NESTED 傳播機(jī)制改為 REQUIRED, 修改代碼如下:

 @Service
    public class UserService {
        @Autowired
        private UserInfoMapper userInfoMapper;
        @Transactional(propagation = Propagation.REQUIRED)
        public void registryUser(String name, String password) {
            //插???信息
            userInfoMapper.insert(name, password);
        }
    }
    @Service
    public class LogService {
        @Autowired
        private LogInfoMapper logInfoMapper;
        @Transactional(propagation = Propagation.REQUIRED)
        public void insertLog(String name, String op) {
            try {
                int a = 10 / 0;
            } catch (Exception e) {
                //回滾當(dāng)前事務(wù)
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            }
            //記錄??操作
            logInfoMapper.insertLog(name, "??注冊");
        }
    }

重新運(yùn)行程序, 發(fā)現(xiàn)用戶表和日志表的數(shù)據(jù)添加都失敗了.
REQUIRED 如果回滾就是回滾所有事務(wù), 不能實(shí)現(xiàn)部分事務(wù)的回滾. (因?yàn)閷儆谕?個(gè)事務(wù))

NESTED和REQUIRED區(qū)別:
• 整個(gè)事務(wù)如果全部執(zhí)行成功, ?者的結(jié)果是?樣的.

• 如果事務(wù)?部分執(zhí)行成功, REQUIRED加入事務(wù)會(huì)導(dǎo)致整個(gè)事務(wù)全部回滾. NESTED嵌套事務(wù)可以實(shí)現(xiàn)局部回滾, 不會(huì)影響上一個(gè)方法中執(zhí)行的結(jié)果.

嵌套事務(wù)之所以能夠?qū)崿F(xiàn)部分事務(wù)的回滾, 是因?yàn)槭聞?wù)中有?個(gè)保存點(diǎn)(savepoint)的概念, 嵌套事務(wù) 進(jìn)入之后相當(dāng)于新建了?個(gè)保存點(diǎn), 而滾回時(shí)只回滾到當(dāng)前保存點(diǎn)。
REQUIRED 是加入到當(dāng)前事務(wù)中, 并沒有創(chuàng)建事務(wù)的保存點(diǎn), 因此出現(xiàn)了回滾就是整個(gè)事務(wù)回滾, 這就是嵌套事務(wù)和加入事務(wù)的區(qū)別

總結(jié)

  1. Spring中使用事務(wù), 有兩種方式: 編程式事務(wù)(手動(dòng)操作)和聲明式事務(wù). 其中聲明式事務(wù)使用較多,在方法上添加 @Transactional 就可以實(shí)現(xiàn)了
  2. 通過 @Transactional(isolation = Isolation.SERIALIZABLE) 設(shè)置事務(wù)的隔離級(jí)別. Spring 中的事務(wù)隔離級(jí)別有 5 種
  3. 通過 @Transactional(propagation = Propagation.REQUIRED) 設(shè)置事務(wù)的傳播機(jī)制, Spring 中的 事務(wù)傳播級(jí)別有 7 種, 重點(diǎn)關(guān)注 REQUIRED (默認(rèn)值) 和 REQUIRES_NEW

以上就是本文全部內(nèi)容,感謝各位能夠看到最后,如有問題,歡迎各位大佬在評論區(qū)指正,希望大家可以有所收獲!創(chuàng)作不易,希望大家多多支持!

到此這篇關(guān)于Spring事務(wù)和事務(wù)傳播機(jī)制操作大全的文章就介紹到這了,更多相關(guān)spring事務(wù)傳播機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Springboot+echarts實(shí)現(xiàn)可視化

    Springboot+echarts實(shí)現(xiàn)可視化

    這篇文章主要為大家詳細(xì)介紹了Springboot+echarts實(shí)現(xiàn)可視化,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • java工具類之實(shí)現(xiàn)java獲取文件行數(shù)

    java工具類之實(shí)現(xiàn)java獲取文件行數(shù)

    這篇文章主要介紹了一個(gè)java工具類,可以取得當(dāng)前項(xiàng)目中所有java文件總行數(shù),代碼行數(shù),注釋行數(shù),空白行數(shù),需要的朋友可以參考下
    2014-03-03
  • Java切割字符串的踩坑實(shí)戰(zhàn)記錄

    Java切割字符串的踩坑實(shí)戰(zhàn)記錄

    最近在項(xiàng)目中使用了java中的分割字符串,踩了一個(gè)坑,充分了展示了自己對java底層的認(rèn)知有很多的不足和欠缺,下面這篇文章主要給大家介紹了關(guān)于Java切割字符串的踩坑實(shí)戰(zhàn)記錄,需要的朋友可以參考下
    2022-11-11
  • SpringBoot處理HTTP請求的詳細(xì)流程

    SpringBoot處理HTTP請求的詳細(xì)流程

    這篇文章主要介紹了SpringBoot處理HTTP請求的詳細(xì)流程,文中通過代碼示例給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-05-05
  • Lombok @Builder注解的兩大陷阱分析及解決

    Lombok @Builder注解的兩大陷阱分析及解決

    Lombok的@Builder注解簡化了建造者模式的代碼編寫,但存在兩個(gè)陷阱:無參構(gòu)造函數(shù)消失和字段默認(rèn)值失效,通過顯式聲明構(gòu)造器和使用@Builder.Default注解,可以解決這些問題
    2026-01-01
  • Maven多模塊項(xiàng)目調(diào)試與問題排查的完整指南

    Maven多模塊項(xiàng)目調(diào)試與問題排查的完整指南

    在現(xiàn)代企業(yè)級(jí)Java開發(fā)中,Maven多模塊項(xiàng)目因其清晰的代碼組織,依賴管理和高效的構(gòu)建流程已成為主流架構(gòu)模式,本文深入剖析多模塊項(xiàng)目的四大核心痛點(diǎn)解決方案,感興趣的小伙伴可以跟隨小編一起了解下
    2025-06-06
  • MyBatis通用Mapper實(shí)現(xiàn)原理及相關(guān)內(nèi)容

    MyBatis通用Mapper實(shí)現(xiàn)原理及相關(guān)內(nèi)容

    今天小編就為大家分享一篇關(guān)于MyBatis通用Mapper實(shí)現(xiàn)原理及相關(guān)內(nèi)容,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • JeecgBoot代碼生成器自定義請求接口實(shí)例

    JeecgBoot代碼生成器自定義請求接口實(shí)例

    文章介紹了如何在使用代碼生成器生成的頁面中添加啟用/停用功能,通過修改List.vue頁面并添加必要的請求頭token來解決身份認(rèn)證失敗的問題,同時(shí),還講述了如何在菜單管理、角色管理中進(jìn)行權(quán)限配置,以確保按鈕功能生效
    2026-01-01
  • Java實(shí)現(xiàn)表單提交(支持多文件同時(shí)上傳)

    Java實(shí)現(xiàn)表單提交(支持多文件同時(shí)上傳)

    本文介紹了Java、Android實(shí)現(xiàn)表單提交(支持多文件同時(shí)上傳)的方法,具有一定的參考價(jià)值,下面跟著小編一起來看下吧
    2017-01-01
  • Spring Boot實(shí)現(xiàn)熱部署的實(shí)例方法

    Spring Boot實(shí)現(xiàn)熱部署的實(shí)例方法

    在本篇文章里小編給大家整理的是關(guān)于Spring Boot實(shí)現(xiàn)熱部署的實(shí)例方法和實(shí)例,需要的朋友們可以參考下。
    2020-02-02

最新評論

益阳市| 治多县| 灵丘县| 突泉县| 溧阳市| 邛崃市| 大英县| 宝坻区| 祁门县| 略阳县| 衡水市| 航空| 金坛市| 开鲁县| 突泉县| 巴林右旗| 崇阳县| 嵊州市| 宝应县| 泊头市| 开封市| 沿河| 大城县| 宁强县| 濮阳县| 香港 | 东丰县| 潞城市| 富顺县| 乌兰察布市| 晋中市| 邓州市| 徐汇区| 临湘市| 陇西县| 桦南县| 兴业县| 辽中县| 兴城市| 萍乡市| 远安县|