Spring事务及其传播机制

目录

前言

1.事务的操作

2.Spring对于事务的实现

2.1 编程式事务(手动版)

2.2声明式事务

  2.2.1 正常操作,没有抛出异常

2.2.2 程序抛出异常,但不做处理

2.2.3 程序报错了,异常捕获

2.2.4 程序报错了,异常捕获,但是不处理,重新抛出异常

2.2.5  @Transactional手动回滚方法

2.2.6 处理完一个内部的异常又出现一个新的异常

3.事务隔离级别

3.1数据库的隔离级别

3.2 Spring事务的隔离级别

4.Spring事务的传播机制

4.1事务传播机制的定义

4.2 Spring事务传播机制有哪些

4.2.1 Required

4.2.2 Requies_new

4.2.3 Never

4.2.4 Nested(嵌套事务)

总结:


前言

   在数据库的时候我们就接触过事物这一概念,简单说事务是一组操作的集合, 是一个不可分割的操作.事务会把所有的操作作为一个整体, 一起向数据库提交或者是撤销操作请求. 所以这组操作要么同时成功, 要么同时失败,事物出现的场景一般都为,操作两个或者多个方法是,需要这些方法要么同时进行要么同时失败.例如银行卡存取钱,我通过ATM机存取钱的时候,后端的数据系统肯定是要进行同步操作的,总不能我通过ATM机取或存了钱,但是由于事故我钱存取了,但是后端数据库却没有变化,这个时候就是"事务"的登场了.

1.事务的操作

   事务的操作主要分为三个步骤

     1. 开启事start transaction/ begin (一组操作前开启事务)
     2. 提交事务: commit (这组操作全部成功, 提交事务)
     3. 回滚事务: rollback (这组操作中间任何一个操作出现异常, 回滚事务)

2.Spring对于事务的实现

   Spring中的事务操作分为两类:一是编程式事务(手写代码去操作事务),二是声明式事务(通过注解来操作事务).

   在进行事务讲解之前我们先创建好数据库以备后续使用

    这里的两张表分别为用户表(user_info) 和 日志表(log_info).

   还有就是在Spring项目中创建好对应的model和mapper

//用户表
@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;
}
@Mapper
public interface UserInfoMapper {
    @Insert("insert into user_info (user_name,password) values (#{userName},#{password})")
    Integer insert (String userName,String password);
}


@Mapper
public interface LogInfoMapper {
    @Insert("insert into log_info (user_name,op) values (#{userName},#{op})")
    Integer insert(String userName,String op);
}
@Service
public class UserService {
    @Autowired
    private UserInfoMapper userInfoMapper;
  
    public Integer insertUser(String userName,String password) {
        return userInfoMapper.insert(userName,password);
    }
}

@Service
public class LogService {
    @Autowired
    private LogInfoMapper logInfoMapper;

    public Integer insertLog (String userName,String op){
        return logInfoMapper.insert(userName,op);
    }
}

  (以上都为前提准备)   

2.1 编程式事务(手动版)

编程式事务主要需要使用到两个类:1.DataSourceTransactionManager  2.TransactionDefinition

SpringBoot 内置了两个对象:
 1. DataSourceTransactionManager 事务管理器. 用来获取事务(开启事务), 提交或回滚事务的
 2. TransactionDefinition 是事务的属性, 在获取事务的时候需要将TransactionDefinition 传递进去从而获得一个事务 TransactionStatus

@Slf4j
@RequestMapping("/user")
@RestController
public class UserController {
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;

    private TransactionDefinition transactionDefinition;//配置

    @Autowired
    private UserService userService;
    @RequestMapping("/registry")
    public String registry(String userName,String password) {
        //开启事务  获取这个操作最开始的状态(如果我后面发生了错误就回到刚开始的状态,就相当于没进行过这个操作)
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        Integer result = userService.insertUser(userName,password);
        log.info("用户插入成功,result:" + result);
        //回滚事务
        dataSourceTransactionManager.rollback(transactionStatus);//回滚到在开启事务时获得的状态

    }
}

   在方法最开始定义的transactionStatus 就相当于一个重生标记,当我后续的操作如果出现了问题就"回溯"到这个时刻,而最开始定义的dataSourceTransactionManager相当于一个启动装置用于去手动的"回滚"或者"提交"事务, 这里就先通过Postman给这一个端口发送一个"userName="张三",password = "123456""的请求看一下响应结果如何

Spring控制台这也是显示SQL语句没有问题的,那来看看数据库中user表数据有没有增加吧.

 由于我上面是将事务进行了回滚,数据库中是没有储存数据的,但是是执行过这一条语句的,为什么这么说呢,当你的表中如果有自增的属性,即使"回滚"了还是触发了自增的,下一次插入数据的时候会默认的往后"退一步"的.

  接下来就将事务进行提交

@Slf4j
@RequestMapping("/user")
@RestController
public class UserController {
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;

    private TransactionDefinition transactionDefinition;//配置

    @Autowired
    private UserService userService;
    @RequestMapping("/registry")
    public String registry(String userName,String password) {
        //开启事务  获取这个操作最开始的状态(如果我后面发生了错误就回到刚开始的状态,就相当于没进行过这个操作)
        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
        Integer result = userService.insertUser(userName,password);
        log.info("用户插入成功,result:" + result);
        //回滚事务
        //dataSourceTransactionManager.rollback(transactionStatus);//回滚到在开启事务时获得的状态

        //提交事务
        dataSourceTransactionManager.commit(transactionStatus);
        return "注册成功";
    }
}

  回滚事务是使用rollback方法,提交事务是使用commit方法

此处的id就不是为1了,而是为2,而且观察一下控制台中当提交事务和回滚事务时 控制台输出的东西是否有差异

  当提交事务成功时是会多出committing这一行的,这也就可以作为判断该事务是提交还是回滚的一个方法了.

   这里模拟一下当方法中出现错误时的场景

@Service
public class LogService {
    @Autowired
    private LogInfoMapper logInfoMapper;

    public Integer insertLog (String userName,String op){
        logInfoMapper.insert(userName,op);
        //模拟失败的情况
        int a = 10/0;
        return 1;
    }
}

此时会导致整体的进行"回滚"

2.2声明式事务

  声明式事务主要是通过@Transactional这个注解来操作的,@Transactional如果是修饰一个方法则会自动的开启事务,提交事务,回滚事务,@Transactional触发回滚事务的条件是方法内抛出异常.

  @Transactional修饰类的时候,就说明这个类下面的每一个方法都被@Transactional修饰.

  下面就来看一下声明式事务在不同场景下所作出的响应

  2.2.1 正常操作,没有抛出异常
//声明式事务
@RestController
@Slf4j
@RequestMapping("/trans")
public class TransController {
    @Autowired
    private UserService userService;

    /**
     * 正常操作,没有抛出异常
     */
    //会自动的进行事务的创建 提交(或回滚)
    @Transactional
    @RequestMapping("/registry")
    public String registry(String userName,String password) {
        Integer result = userService.insertUser(userName,password);
        log.info("用户插入成功,result:" + result);

        return "注册成功";
    }
}

 事务是正常提交的,没有出现任何问题.

2.2.2 程序抛出异常,但不做处理
  /**
     * 抛出异常
     * 程序报错,没有处理
     */
    @Transactional
    @RequestMapping("/registry2")
    public String registry2(String userName,String password) {
        Integer result = userService.insertUser(userName,password);
        log.info("用户插入成功,result:" + result);
        int a = 10/0;
        return "注册成功";
    }

 由此可知声明式事务当被@Transactional修饰的方法中抛出遗产时,就会直接触发回滚,且请求方也会获得对应的请求失败的信息.

2.2.3 程序报错了,异常捕获
    /**
     * 程序报错了,异常捕获,但是不处理,重新抛出异常
     */
    @Transactional
    @RequestMapping("/registry4")
    public String registry4(String userName,String password) {
        Integer result = userService.insertUser(userName,password);
        log.info("用户插入成功,result:" + result);
        try {
            int a = 10/0;
        } catch (Exception e) {
            log.info("程序出错");
            throw e;
        }
        return "注册成功";
    }

PostMan接收的Body是显示注册成功的

后端控制台这块也是成功提交了事务

所以当方法内抛出异常时,有对异常进行捕获就不会触发事务的回滚

2.2.4 程序报错了,异常捕获,但是不处理,重新抛出异常
    @Transactional
    @RequestMapping("/registry4")
    public String registry4(String userName,String password) {
        Integer result = userService.insertUser(userName,password);
        log.info("用户插入成功,result:" + result);
        try {
            int a = 10/0;
        } catch (Exception e) {
            log.info("程序出错");
            throw e;
        }
        return "注册成功";
    }

 此处将异常进行捕获之后再将异常抛出

这种方式一样会触发@Transactional的回滚机制的.

2.2.5  @Transactional手动回滚方法

   @Transactional的自动回滚机制确实实现的很好,但是有些场景需要触发回滚的时候,当时却不满足 @Transactional的回滚触发机制怎么办,就例如当这个方法运行到一些结果的时候,在我的逻辑里面是错误的,需要触发回滚,此时有两种方法: 1.是抛出一个异常,达到Transactional触发回滚的条件,这种方法会有点粗暴. 2.Spring也是考虑到了这一点,提供了TransactionAspectSupport类以便我们进行回滚操作

    /**
     * 手动回滚
     */
    @Transactional
    @RequestMapping("/registry5")
    public String registry5(String userName,String password) {
        Integer result = userService.insertUser(userName,password);
        log.info("用户插入成功,result:" + result);
        try {
            int a = 10/0;
        } catch (Exception e) {
            log.info("程序出错");
            //手动进行回滚  不报错
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return "注册成功";
    }

  此处将异常捕获了之后,然后使用TransactionAspectSupport去触发回滚

 此时的控制台就看上去会比较整洁一些

2.2.6 处理完一个内部的异常又出现一个新的异常
    @Transactional
    @RequestMapping("/registry6")
    public String registry6(String userName,String password) throws IOException {
        Integer result = userService.insertUser(userName,password);
        log.info("用户插入成功,result:" + result);
        try {
            int a = 10/0;
        } catch (Exception e) {
            log.info("程序出错");
            throw new IOException();
        }
        return "注册成功";
    }

 此处先抛出一个IOException的异常

postman接收的body报错没问题,那来看看Transactional是否触发了回滚机制

  此处Transactional很明显没有触发回滚机制,那我们把抛出的异常换一换看一下得到的结果是否会发生变化

    @Transactional
    @RequestMapping("/registry6")
    public String registry6(String userName,String password) throws IOException {
        Integer result = userService.insertUser(userName,password);
        log.info("用户插入成功,result:" + result);
        try {
            int a = 10/0;
        } catch (Exception e) {
            log.info("程序出错");
            throw new NullPointerException();
        }
        return "注册成功";
    }

postman不用看,直接去观察Spring控制台的结果

   此时Transactional触发了回滚机制,导致这一现象的产生主要是Transactional中的rollbackFor在起作用.

   @Transactional 默认只在遇到运行时异常和Error时才会回滚, 非运行时异常不回滚,即 Exception的子类中, 除了RuntimeException及其子类.

  也就是说当我抛出一个非运行时异常的时候,Transactional是无法触发回滚的,如果想要抛出非运行异常的时候也一样触发回滚就要设置一下Transactional中的rollbackFor

    @Transactional(rollbackFor = {Exception.class,Error.class})//去设置哪些异常抛出的时候需要进行回滚
    @RequestMapping("/registry7")
    public String registry7(String userName,String password) throws IOException {
        Integer result = userService.insertUser(userName,password);
        log.info("用户插入成功,result:" + result);
        try {
            int a = 10/0;
        } catch (Exception e) {
            log.info("程序出错");
            throw new IOException();
        }
        return "注册成功";
    }

  rollbackFor是一个集合,可以设置多个.

  此时就再来试试,抛出IOException时是否会触发回滚机制了

成功触发了回滚机制

3.事务隔离级别

3.1数据库的隔离级别

  在简介Spring事务隔离级别之前,先来回顾一下数据库时期学到的隔离级别        

  SQL 标准定义了四种隔离级别, MySQL 全都支持. 这四种隔离级别分别是:

1. 读未提交(READ UNCOMMITTED): 读未提交, 也叫未提交读. 该隔离级别的事务可以看到其他事务中
    未提交的数据:
    因为其他事务未提交的数据可能会发生回滚, 但是该隔离级别却可以读到, 我们把该级别读到的数据称之为脏数据, 这个问题称之为脏读.

2. 读提交(READ COMMITTED): 读已提交, 也叫提交读. 该隔离级别的事务能读取到已经提交事务的数据,
    该隔离级别不会有脏读的问题.但由于在事务的执行中可以读取到其他事务提交的结果, 所以在不
同时间的相同 SQL 查询可能会得到不同的结果, 这种现象叫做不可重复读

3. 可重复读(REPEATABLE READ): 事务不会读到其他事务对已有数据的修改, 即使其他事务已提交. 也就可以确保同一事务多次查询的结果一致, 但是其他事务新插入的数据, 是可以感知到的. 这也就引发了幻读问题. 可重复读, 是 MySQL 的默认事务隔离级别.
比如此级别的事务正在执行时, 另一个事务成功的插入了某条数据, 但因为它每次查询的结果都是
一样的, 所以会导致查询不到这条数据, 自己重复插入时又失败(因为唯一约束的原因). 明明在事务
中查询不到这条信息,但自己就是插入不进去, 这个现象叫幻读.

4. 串行化(SERIALIZABLE): 序列化, 事务最高隔离级别. 它会强制事务排序, 使之不会发生冲突, 从而解决了脏读, 不可重复读和幻读问题, 但因为执行效率低, 所以真正使用的场景并不多.

3.2 Spring事务的隔离级别

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

  DEFAULT是Transactional默认的隔离级别,如果想要修改Spring事务的隔离级别可以通过Transactional中的isolation这个属性进行修改

    @Transactional(rollbackFor = {Exception.class,Error.class},isolation = Isolation.DEFAULT)//去设置哪些异常抛出的时候需要进行回滚
    @RequestMapping("/registry7")
    public String registry7(String userName,String password) throws IOException {
        Integer result = userService.insertUser(userName,password);
        log.info("用户插入成功,result:" + result);
        try {
            int a = 10/0;
        } catch (Exception e) {
            log.info("程序出错");
            throw new IOException();
        }
        return "注册成功";
    }

4.Spring事务的传播机制

4.1事务传播机制的定义

  事务传播机制就是: 多个事务方法存在调用关系时, 事务是如何在这些方法间进行传播的.

  比如有两个方法A, B都被@Transactional 修饰, A方法调用B方法A方法运行时, 会开启一个事务. 当A调用B时, B方法本身也有事务, 此时B方法运行时, 是加入A的事务, 还是创建一个新的事务呢?
这个就涉及到了事务的传播机制.

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

4.2 Spring事务传播机制有哪些

@Transactional 注解支持事务传播机制的设置, 通过 propagation 属性来指定传播行为.
   Spring 事务传播机制有以下 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 .

4.2.1 Required

  先来看一下默认情况下(REQUIRED)的表现情况

@Service
public class UserService {
    @Autowired
    private UserInfoMapper userInfoMapper;
    @Transactional
    public Integer insertUser(String userName,String password) {
        return userInfoMapper.insert(userName,password);
    }
}
@Service
public class LogService {
    @Autowired
    private LogInfoMapper logInfoMapper;

    @Transactional
    public Integer insertLog (String userName,String op){
        return logInfoMapper.insert(userName,op);
    }
}
@Slf4j
@RequestMapping("/proga")
@RestController
public class ProController {
    @Autowired
    private UserService userService;
    @Autowired
    private LogService logService;

    @Transactional
    @RequestMapping("/p1")
    public String registry(String userName,String password) {
        Integer result = userService.insertUser(userName,password);
        log.info("用户插入成功,result:" + result);

        Integer result2 = logService.insertLog(userName,"用户自行注册");
        log.info("日志表插入成功,result:" + result2);
        return "注册成功";
    }

}

先看看事务中没有抛出异常的情况

此时两张表都更新了数据,那如果我在insertLog故意制造一个错误会是怎么样的.

@Service
public class LogService {
    @Autowired
    private LogInfoMapper logInfoMapper;

    @Transactional
    public Integer insertLog (String userName,String op){
        logInfoMapper.insert(userName,op);
        //模拟失败的情况
        int a = 10/0;
        return 1;
    }
}

  此时两个表的数据都没有进行更新,也就是说insertLog里面出现了错误触发了回滚影响到了insertUser事务的提交了,REQUIRED:如果其中一个失败,那就整体失败.insertUser  和 insertLog 都是使用了默认的Transactional,而且都到了同一个方法中,所以它们中任何一个事务失败就会导致整体事务的失败(insertUser  和 insertLog 都集中到了一个事务中)

4.2.2 Requies_new

  先来看一下介绍,Requies_new:创建一个新的事务. 如果当前存在事务, 则把当前事务挂起.从字面上的意思就是,管你有没有事务,反正我自己独立出来,不归你管,

  这里就把insertUser方法的propagation给设置成Requies_new,先看一下正常情况下的情况

 正常情况下Requies_new和Required的唯一区别就是,Required是将两个事务统一提交的(毕竟是将它们放到了一个集合中),Requies_new的时候insertUser和insertLog的事务是分开提交的(这里insertUser和insertLog不处于同一个集合中).

  那来看一下insertLog再次出现错误是否会影响到insertUser事务的提交

返回的body报错

  这里就很明显的可以看出insertUser的事务正常提交了,而insertLog的事务由于抛出了异常进行了回滚,来看看数据库那边

  符合场景

   方法和insertLog还是使用默认的传播机制,就将这两个(方法和insertLog)视为一个整体,这两个其中一个抛出异常都是会触发回滚的,但是insertUser的事务传播机制为required_new 相当于自己去单开了一条线路,这与上面方法加insertLog形成的集合互不影响.

   如果将insertLog的隔离级别也换成required_new的话,这三者的关系就相当于三个独立的个体 都是互不影响的

4.2.3 Never

  先看看Never的介绍:以非事务方式运行如果当前存在事务,则抛出异常

 这里Never的意思简单点就是这个方法不能以事务的方式进行运行(不能被Transactional注解修饰),如果有的话就都别玩

  变动代码:

@Service
public class LogService {
    @Autowired
    private LogInfoMapper logInfoMapper;

    @Transactional(propagation = Propagation.NEVER)
    public Integer insertLog (String userName,String op){
        logInfoMapper.insert(userName,op);
        //模拟失败的情况
        int a = 10/0;
        return 1;
    }
}

再来看看运行起来是啥情况

这里可以看见insertUser的事务(insertUser的传播机制还是为required_new)是正常提交的,接下来来研究研究报错信息

insertLog也跟就没有运行起来就别说事务的回滚了

跟事务隔离级别为Nerve的方法所在同一个方法底下的东西都不能有事务

4.2.4 Nested(嵌套事务)

   照例先看一下介绍:如果当前存在事务, 则创建一个事务作为当前事务的嵌套事务来运行.
如果当前没有事务, 则该取值等价于 PROPAGATION_REQUIRED,

   此处就将insertUser和insertLog的传播机制都设置为Nested

@Service
public class UserService {
    @Autowired
    private UserInfoMapper userInfoMapper;
    @Transactional(propagation = Propagation.NESTED)
    public Integer insertUser(String userName,String password) {
        return userInfoMapper.insert(userName,password);
    }
}
@Service
public class LogService {
    @Autowired
    private LogInfoMapper logInfoMapper;

    @Transactional(propagation = Propagation.NESTED)
    public Integer insertLog (String userName,String op){
        logInfoMapper.insert(userName,op);
        //模拟失败的情况
        int a = 10/0;
        return 1;
    }
}

  代码正常运行(没有抛出异常)的情况就不进行演示了,不用想都知道是两个事务正常提交的,下面就来试一下insertLog中抛出异常的场景.

@Service
public class LogService {
    @Autowired
    private LogInfoMapper logInfoMapper;

    @Transactional(propagation = Propagation.NESTED)
    public Integer insertLog (String userName,String op){
        logInfoMapper.insert(userName,op);
        //模拟失败的情况
        int a = 10/0;
        return 1;
    }
}

  这里就会发现这不跟默认(Required)的情况一样吗?对则全对,错则全错.

  这里还有一种情况没有是探讨,就是当我insertUser或者insertLog里面抛出异常后,如果进行解决了会在怎么样,

@Slf4j
@Service
public class LogService {
    @Autowired
    private LogInfoMapper logInfoMapper;

    @Transactional(propagation = Propagation.NESTED)
    public Integer insertLog (String userName,String op){
        logInfoMapper.insert(userName,op);
        //模拟失败的情况
        try {
            int a = 10/0;
        } catch (Exception e) {
            log.info("insertLog中的异常已进行处理,并且回滚");
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return 1;
    }
}

  此时就会发现user_info表中是正常添加了数据的,而log_info由于回滚没有进行数据的添加,这就是Nested的特性,Nested修饰了insertUser和insertLog 且这两个方法在被事务修饰的方法里面执行,此时就相当于一个"大公司"(主方法)旗下的两个"子公司"(insertUser和insertLog),如果这两个子公司其中一个或者全部破产了(或出现严重事故了),子公司没有制定相应的对策进行解决,则后续一定会影响到我大公司(主方法)的运作,从而会导致大公司旗下的其他小公司("其他传播机制为Nested的方法"),导致整体的崩盘.如果我出事的小公司在出现问题时就将问题进行了处理("对异常进行捕获")的话就不会影响到其他模块.而如果传播机制为Required(默认情况的话),在一样的场景即使我对异常进行了捕获还是会导致整体的回滚.

@Service
public class UserService {
    @Autowired
    private UserInfoMapper userInfoMapper;
    @Transactional(propagation = Propagation.REQUIRED)
    public Integer insertUser(String userName,String password) {
        return userInfoMapper.insert(userName,password);
    }
}


@Slf4j
@Service
public class LogService {
    @Autowired
    private LogInfoMapper logInfoMapper;

    @Transactional(propagation = Propagation.REQUIRED)
    public Integer insertLog (String userName,String op){
        logInfoMapper.insert(userName,op);
        //模拟失败的情况
        try {
            int a = 10/0;
        } catch (Exception e) {
            log.info("insertLog中的异常已进行处理,并且回滚");
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return 1;
    }
}

   这就是Nested与Required的区别,在没有异常抛出和抛出异常不进行处理的时候,这两者是一样的,但如果有对异常进行捕获或者其他处理的话,此时就有着天壤之别了.

NESTED和REQUIRED区别
1.整个事务如果全部执行成功,二者的结果是一样的
2.如果事务一部分执行成功REQUIRED加入事务会导致整个事务全部回滚NESTED嵌套事务可以实现局部回滚不会影响上一个方法中执行的结果

总结:

    Spring事务的传播机制的学习对了解Spring起到了一定的作用,在传播机制这块主要掌握的机制大致就是我上述的4个Required,Requies_new,Never,Nested,这四种,事务的操作的话,就尽量使用声明式的方式来,Spring方便的地方就是在于有大量的注解以便简化我们的操作和代码量,最后如果本篇文章让你学到了东西就麻烦佬给个赞吧.

  • 40
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
### 回答1: 在Spring中,事务传播机制指的是当一个方法调用另一个方法时,当前方法已经开启了事务,而新方法又该如何处理事务的行为。Spring定义了7种事务传播行为,它们分别是: 1. PROPAGATION_REQUIRED:如果当前存在一个事务,则加入该事务;如果当前没有事务,则创建一个新的事务。 2. PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,则加入该事务;如果当前没有事务,则非事务执行。 3. PROPAGATION_MANDATORY:强制要求当前存在事务,如果当前没有事务,则抛出异常。 4. PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则挂起该事务。 5. PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,则挂起该事务。 6. PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。 7. PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则创建一个新的事务。 以上是Spring中定义的7种事务传播行为,开发者可以根据具体的业务需求来选择不同的事务传播行为,以保证数据的一致性和完整性。 ### 回答2: Spring事务传播机制是用来控制多个事务方法之间的事务行为的。在Spring中,事务可以在方法级别或类级别进行定义和使用。 Spring事务传播机制有以下几种: 1. REQUIRED(默认):如果当前存在事务,则加入到当前事务中执行;如果当前没有事务,则开启一个新的事务进行执行。 2. SUPPORTS:如果当前存在事务,则加入到当前事务中执行;如果当前没有事务,则以非事务方式执行。 3. MANDATORY:如果当前存在事务,则加入到当前事务中执行;如果当前没有事务,则抛出异常。 4. REQUIRES_NEW:开启一个新的事务执行,如果当前存在事务,则挂起当前事务。 5. NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,则挂起当前事务。 6. NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。 7. NESTED:以嵌套事务方式执行,如果当前存在事务,则开启一个子事务执行;如果当前没有事务,则开启一个新的事务进行执行。 事务传播机制允许在多个事务方法之间灵活地管理事务的行为。可以根据业务需求选择不同的传播机制来控制事务的边界和范围。通过合理地使用事务传播机制,可以确保事务的一致性和完整性,提高系统的数据一致性和可靠性。 ### 回答3: Spring事务传播机制是指在多个事务方法调用的场景下,事务如何传播和管理的机制。Spring提供了多种事务传播机制来适应不同的业务需求。 1. REQUIRED(默认):如果当前没有事务存在,则创建一个新的事务;如果当前已经存在事务,则加入该事务并参与到事务的提交或回滚中。这是最常用的传播机制,适用于需要确保一组方法在同一个事务中执行的情况。 2. SUPPORTS:如果当前存在事务,则加入该事务并参与到事务的提交或回滚中;如果当前没有事务存在,则以非事务方式执行。适用于不需要事务的方法,但如果已存在事务则可以加入。 3. MANDATORY:如果当前存在事务,则加入该事务并参与到事务的提交或回滚中;如果当前没有事务存在,则抛出异常。适用于需要强制事务存在的方法。 4. REQUIRES_NEW:如果当前存在事务,则挂起当前事务,并创建一个新的事务执行;如果当前没有事务存在,则创建一个新的事务。适用于需要保证方法在一个新的事务中执行的情况,不受外部事务的影响。 5. NOT_SUPPORTED:以非事务方式执行方法,如果当前存在事务,则将当前事务挂起。适用于不需要事务的方法,同时不希望受到外部事务的影响。 6. NEVER:以非事务方式执行方法,如果当前存在事务,则抛出异常。适用于不希望在事务环境中执行的方法。 7. NESTED:如果当前存在事务,则在当前事务的嵌套事务中执行;如果当前没有事务存在,则创建一个新的事务。适用于需要在一个独立的嵌套事务中执行的方法,嵌套事务可以独立于外部事务的提交或回滚。 通过合理选择和配置事务传播机制,可以实现对事务的灵活管理,确保数据的一致性和完整性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值