0


【Spring】Spring事务和事务传播机制

🔥个人主页: 中草药

🔥专栏:【Java】登神长阶 史诗般的Java成神之路


一、Spring事务

我们在MySQL阶段已经学习了MySQL的事务相关知识,详情可见

【MySQL数据库】索引与事务-CSDN博客

1、概念

我们在此做一个简单回顾

事务(Transaction)是指一组操作的集合,这些操作作为一个整体,要么全部成功,要么全部失败,具有以下四大特性(ACID):

  1. 原子性(Atomicity):事务中的操作要么全部成功,要么全部失败,不可分割。
  2. 一致性(Consistency):事务执行前后,数据处于一致状态。
  3. 隔离性(Isolation):事务相互独立执行,不受其他事务的干扰。
  4. 持久性(Durability):事务完成后,数据的更改会被永久保存。

在分布式环境或复杂业务逻辑中,事务可以有效防止数据异常,提高系统的可靠性。

2、事务的实现

1、编程式事务

使用DataSourceTransactionManager和TransactionDefinition去进行事务的管理

DataSourceTransactionManager 事务管理器. ⽤来获取事务(开启事务), 提交或回滚事务,

TransactionDefinition 是事务的属性, 在获取事务的时候需要将 TransactionDefinition 传递进去从而获得⼀个事务 TransactionStatus

package org.example.controller;

import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/user")
@RestController
public class UserController {
    @Autowired
    private UserService userService;

    @Autowired
    private DataSourceTransactionManager transactionManager;

    @Autowired
    private TransactionDefinition transactionDefinition;

    @RequestMapping("/registry")
    public String registry(String name, String password) {
        //⽤⼾注册
        TransactionStatus status = transactionManager.getTransaction(transactionDefinition);
        userService.registryUser(name, password);
        //提交事务
        //transactionManager.commit(status);
        //回滚事务
        transactionManager.rollback(status);
        return "注册成功";
    }
}

2、声明式事务(推荐):

需要先在Maven中添加依赖

<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-tx</artifactId>
</dependency>
  • 通过 @Transactional 注解实现事务管理,配置简单,非侵入式。
  • 配合 AOP 切面拦截方法调用,统一控制事务逻辑。
@Transactional
public void someTransactionalMethod() {
    // 业务逻辑
}
    在需要事务的⽅法上添加 @Transactional 注解就可以实现了. ⽆需⼿动开启事务和提交事 

务, 进⼊⽅法时⾃动开启事务, ⽅法执⾏完会⾃动提交事务, 如果中途发⽣了没有处理的异常会⾃动

回滚事务.

测试代码

package org.example.controller;

import lombok.extern.slf4j.Slf4j;
import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

@RestController
@Slf4j
@RequestMapping("/trans")
public class TransactionalController {
    @Autowired
    private UserService userService;

    /**
     * 不加事务的情况下, 数据插入成功
     * @param name
     * @param password
     * @return
     */
//    @Transactional
    @RequestMapping("/r1")
    public String registry(String name,String password){
        //用户注册
        userService.registryUser(name,password);
        log.info("数据插入成功");
        int a = 10/0;
        return "注册成功";
    }

    /**
     * 添加事务的情况, 事务回滚
     */
    @Transactional
    @RequestMapping("/r2")
    public String r2(String name,String password){
        //用户注册
        userService.registryUser(name,password);
        log.info("数据插入成功");
        int a = 10/0;
        return "注册成功";
    }

    /**
     * 异常被捕获: 事务提交
     */
    @Transactional
    @RequestMapping("/r3")
    public String r3(String name,String password){
        //用户注册
        userService.registryUser(name,password);
        log.info("数据插入成功");
        try {
            int a = 10/0;
        }catch (Exception e){
            log.error("发生异常");
        }

        return "注册成功";
    }

    /**
     * 异常被捕获并且throw: 事务回滚
     */
    @Transactional
    @RequestMapping("/r4")
    public String r4(String name,String password){
        //用户注册
        userService.registryUser(name,password);
        log.info("数据插入成功");
        try {
            int a = 10/0;
        }catch (Exception e){
            log.error("发生异常");
            throw e;
        }
        return "注册成功";
    }

    /**
     * 手动回滚事务
     */
    @Transactional
    @RequestMapping("/r5")
    public String r5(String name,String password){
        //用户注册
        userService.registryUser(name,password);
        log.info("数据插入成功");
        try {
            int a = 10/0;
        }catch (Exception e){
            log.error("发生异常");
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return "注册成功";
    }

    /**
     * 发生非运行时异常, 事务提交
     */
    @Transactional
    @RequestMapping("/r6")
    public String r6(String name,String password) throws IOException {
        //用户注册
        userService.registryUser(name,password);
        log.info("数据插入成功");
        if (true){
            throw new IOException();
        }

        return "注册成功";
    }
    /**
     * 设置rollbackFor, 发生非运行时异常, 事务回滚
     */
    @Transactional(rollbackFor = Exception.class)
    @RequestMapping("/r7")
    public String r7(String name,String password) throws IOException {
        //用户注册
        userService.registryUser(name,password);
        log.info("数据插入成功");
        if (true){
            throw new IOException();
        }

        return "注册成功";
    }

    /**
     * 设置事务的隔离级别
     *
     */
    @Transactional(isolation = Isolation.DEFAULT)
    @RequestMapping("/r8")
    public String r8(String name,String password) throws IOException {
        //用户注册
        userService.registryUser(name,password);
        log.info("数据插入成功");
        if (true){
            throw new IOException();
        }
        return "注册成功";
    }
}

@Transactional 默认只在遇到运行时异常和Error时才会回滚, ⾮运行时异常不回滚. 即

Exception的⼦类中, 除了RuntimeException及其⼦类

3、Spring事务的隔离级别

事务隔离级别描述了并发事务之间数据可见性的限制程度,直接影响事务操作的正确性和系统性能。隔离级别越高,数据一致性越强,但性能越低。SQL 标准定义了以下四种隔离级别:
隔离级别问题性能说明读未提交(Read Uncommitted)脏读、不可重复读、幻读高事务可以读取其他事务未提交的数据,数据一致性最低。读已提交(Read Committed)不可重复读、幻读较高事务只能读取其他事务已经提交的数据,避免了脏读,但仍存在不可重复读和幻读问题。可重复读(Repeatable Read)幻读中等事务在读取数据时,保证同一事务内多次读取结果一致,避免了脏读和不可重复读,但可能出现幻读。串行化(Serializable)无问题低最高隔离级别,事务顺序执行,完全避免脏读、不可重复读和幻读,但性能较差,可能导致大量锁冲突。
Spring中的事务隔离级别有5种:

1、Isolation.DEFAULT : 以连接的数据库的事务隔离级别为主.

2、Isolation.READ_UNCOMMITTED : 读未提交, 对应SQL标准中 READ UNCOMMITTED

3、Isolation.READ_COMMITTED : 读已提交,对应SQL标准中 READ COMMITTED

4、Isolation.REPEATABLE_READ : 可重复读, 对应SQL标准中 REPEATABLE READ

5、Isolation.SERIALIZABLE : 串⾏化, 对应SQL标准中 SERIALIZABLE

Spring 中事务隔离级别可以通过 @Transactional 中的 isolation 属性进行设置

    @Transactional(isolation = Isolation.READ_COMMITTED)
    @RequestMapping("/r3")
    public String r3(String name,String password){
        //用户注册
        userService.registryUser(name,password);
        log.info("数据插入成功");
        try {
            int a = 10/0;
        }catch (Exception e){
            log.error("发生异常");
        }

        return "注册成功";
    }

二、事务传播机制

1、概念

    事务传播机制定义了在一个事务方法被另一个事务方法调用时,事务如何在这些方法之间传播。例如,当一个事务方法
A

调用另一个事务方法

B

时,

B

方法是加入

A

方法的现有事务,还是开启一个新的事务,或者以其他方式处理事务,这就是由事务传播机制决定的。

Spring 定义了七种事务传播行为,通过

@Transactional

注解的

propagation

属性来指定。

事务隔离级别解决的是多个事务同时调用同一个数据库的问题

事务传播机制解决的是同一个事务在多个节点(方法)中传递的问题

2、传播行为类型

@Transaction 注解支持事务传播机制的设置,通过propagation 属性来制定传播机制的设置,事务的传播机制有以下7种:

1、Propagation.REQUIRED : 默认的事务传播级别. 如果当前存在事务, 则加⼊该事务. 如果当前没 有事务, 则创建⼀个新的事务.

2、Propagation.SUPPORTS : 如果当前存在事务, 则加入该事务. 如果当前没有事务, 则以⾮事务的方式继续运行.

3、Propagation.MANDATORY :强制性. 如果当前存在事务, 则加⼊该事务. 如果当前没有事务,抛出异常.

4、Propagation.REQUIRES_NEW : 创建⼀个新的事务. 如果当前存在事务, 则把当前事务挂起. 也就是说不管外部⽅法是否开启事务, Propagation.REQUIRES_NEW 修饰的内部⽅法都会新开启⾃⼰的事务, 且开启的事务相互独⽴, 互不干扰。

5、Propagation.NOT_SUPPORTED : 以⾮事务⽅式运行, 如果当前存在事务, 则把当前事务挂起(不⽤).

6、
Propagation.NEVER : 以非事务的方式运行,如果当前存在事务,则抛出异常。

7、Propagation.NESTED
: 如果当前存在事务, 则创建⼀个事务作为当前事务的嵌套事务来运⾏.
如果当前没有事务, 则该取值等价于 PROPAGATION_REQUIRED

举例

REQUIRED(默认)
如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是最常用的事务传播行为。例如:

@Service
@Transactional
public class OuterService {

    @Autowired
    private InnerService innerService;

    public void outerMethod() {
        // 执行一些业务操作
        //...
        innerService.innerMethod();
        // 继续执行其他业务操作
        //...
    }
}

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class InnerService {

    public void innerMethod() {
        // 执行内部业务操作
        //...
    }
}

在上述示例中,当

outerMethod

方法被调用时,如果当前存在事务(例如,在一个更大的业务流程中已经开启了事务),则

innerMethod

方法将加入该事务;如果

outerMethod

方法是在没有事务的环境下被调用,则会为

outerMethod

innerMethod

创建一个新的事务。

REQUIRES_NEW
总是创建一个新的事务。如果当前存在事务,则将当前事务挂起,新事务执行完毕后再恢复原事务。例如:

@Service
@Transactional
public class OuterService {

    @Autowired
    private InnerService innerService;

    public void outerMethod() {
        // 执行一些业务操作
        //...
        try {
            innerService.innerMethod();
        } catch (Exception e) {
            // 处理innerMethod方法抛出的异常,但不影响outerMethod方法所在的事务
            //...
        }
        // 继续执行其他业务操作
        //...
    }
}

@Service
@Transactional(propagation = Propagation.REQUIRES_NEW)
public class InnerService {

    public void innerMethod() {
        // 执行内部业务操作
        //...
        if (true) {
            throw new RuntimeException("Inner method error");
        }
    }
}

在这个例子中,

innerMethod

方法总是会开启一个新的事务,即使

outerMethod

方法已经在一个事务中。如果

innerMethod

方法抛出异常并回滚,

outerMethod

方法所在的事务不会受到影响,因为

innerMethod

的事务是独立的。

SUPPORTS
如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。例如:

@Service
@Transactional
public class OuterService {

    @Autowired
    private InnerService innerService;

    public void outerMethod() {
        // 执行一些业务操作
        //...
        innerService.innerMethod();
        // 继续执行其他业务操作
        //...
    }
}

@Service
@Transactional(propagation = Propagation.SUPPORTS)
public class InnerService {

    public void innerMethod() {
        // 执行内部业务操作
        //...
    }
}

outerMethod

在事务中调用

innerMethod

时,

innerMethod

会加入

outerMethod

的事务;如果

outerMethod

没有在事务中调用

innerMethod

,则

innerMethod

会以非事务方式执行。

NOT_SUPPORTED
总是以非事务方式执行,如果当前存在事务,则将当前事务挂起。例如:

@Service
@Transactional
public class OuterService {

    @Autowired
    private InnerService innerService;

    public void outerMethod() {
        // 执行一些业务操作
        //...
        innerService.innerMethod();
        // 继续执行其他业务操作
        //...
    }
}

@Service
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class InnerService {

    public void innerMethod() {
        // 执行内部业务操作
        //...
    }
}

在这种情况下,

innerMethod

永远不会在事务中执行,即使

outerMethod

处于事务环境中。

MANDATORY
如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。例如:

@Service
@Transactional
public class OuterService {

    @Autowired
    private InnerService innerService;

    public void outerMethod() {
        // 执行一些业务操作
        //...
        innerService.innerMethod();
        // 继续执行其他业务操作
        //...
    }
}

@Service
@Transactional(propagation = Propagation.MANDATORY)
public class InnerService {

    public void innerMethod() {
        // 执行内部业务操作
        //...
    }
}

如果

outerMethod

没有在事务中调用

innerMethod

,将会抛出

IllegalTransactionStateException

异常,因为

innerMethod

要求必须在事务环境中执行。

NEVER
总是以非事务方式执行,如果当前存在事务,则抛出异常。例如:

@Service
@Transactional
public class OuterService {

    @Autowired
    private InnerService innerService;

    public void outerMethod() {
        // 执行一些业务操作
        //...
        innerService.innerMethod();
        // 继续执行其他业务操作
        //...
    }
}

@Service
@Transactional(propagation = Propagation.NEVER)
public class InnerService {

    public void innerMethod() {
        // 执行内部业务操作
        //...
    }
}

如果

outerMethod

在事务中调用

innerMethod

,将会抛出

IllegalTransactionStateException

异常。

NESTED
如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则创建一个新的事务。嵌套事务是外部事务的一个子事务,它有自己的保存点。如果子事务回滚,只会回滚到保存点,而不会影响外部事务;如果外部事务回滚,则子事务也会回滚。例如:

@Service
@Transactional
public class OuterService {

    @Autowired
    private InnerService innerService;

    public void outerMethod() {
        // 执行一些业务操作
        //...
        try {
            innerService.innerMethod();
        } catch (Exception e) {
            // 处理innerMethod方法抛出的异常,但不影响outerMethod方法所在的事务
            //...
        }
        // 继续执行其他业务操作
        //...
    }
}

@Service
@Transactional(propagation = Propagation.NESTED)
public class InnerService {

    public void innerMethod() {
        // 执行内部业务操作
        //...
        if (true) {
            throw new RuntimeException("Inner method error");
        }
    }
}

在上述示例中,如果

innerMethod

抛出异常并回滚,只会回滚

innerMethod

中的操作,

outerMethod

中的其他操作不受影响;如果

outerMethod

整体回滚,则

innerMethod

的操作也会回滚。


    Spring 事务管理及其事务传播机制为企业级应用开发提供了强大而灵活的事务处理能力。通过合理地配置事务和选择合适的事务传播行为,可以有效地保证数据的一致性、完整性和隔离性,同时满足复杂业务逻辑和分布式系统的需求。在实际开发中,我们需要深入理解事务传播机制的原理和应用场景,根据业务需求进行准确的配置,以构建健壮、可靠的应用程序。

梦想家命长,实干家寿短。——约.奥赖利

🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀

以上,就是本期的全部内容啦,若有错误疏忽希望各位大佬及时指出💐

制作不易,希望能对各位提供微小的帮助,可否留下你免费的赞呢🌸

标签: spring mybatis java

本文转载自: https://blog.csdn.net/2302_79806056/article/details/144232232
版权归原作者 中草药z 所有, 如有侵权,请联系我们删除。

“【Spring】Spring事务和事务传播机制”的评论:

还没有评论