【Spring Boot】Spring 事务探秘:核心机制与应用场景解析

前言

🌟🌟本期讲解关于spring 事务介绍~~~

🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客

🔥 你的点赞就是小编不断更新的最大动力

🎆那么废话不多说直接开整吧~~

目录

📚️1.事务

🚀1.1什么是事务

🚀1.2为什么需要事务

🚀1.3操作事务

📚️2.Spring事务

🚀2.1编程式事务

🚀2.2声明式事务

1.异常捕获

2.异常捕获再次抛出

3.编译时异常

4.运行时异常

5@transaction属性

6.@transaction总结

📚️3.总结

📚️1.事务

🚀1.1什么是事务

事务是⼀组操作的集合, 是⼀个不可分割的操作.

事务会把所有的操作作为⼀个整体, ⼀起向数据库提交或者是撤销操作请求. 所以这组操作要么同时成功, 要么同时失败

🚀1.2为什么需要事务

假如有以下场景,若在存钱的过程中,A账户减少了100元,本应该就是在B账户中多出100元,但是

如果没有事务,第⼀步执⾏成功了, 第⼆步执⾏失败了, 那么A 账⼾的100 元就平⽩⽆故消失了. 如果使⽤事务就可以解决这个问题, 让这⼀组操作要么⼀起成功, 要么⼀起失败

就可以解决上述的问题了;

🚀1.3操作事务

在数据库中操作事务有以下几个步骤:

  1. 开启事start transaction/ begin (⼀组操作前开启事务)

  2. 提交事务: commit (这组操作全部成功, 提交事务)

  3. 回滚事务: rollback (这组操作中间任何⼀个操作出现异常, 回滚事务)

当然这是在MySQL阶段学习的几个事务操作步骤,但是spring也是有事务的操作的,那么我们接着往下面看看~~~

📚️2.Spring事务

MySQL对于事务进行了实现,Spring对事务也是进行了实现,具体有两种实现方式:

Spring 中的事务操作分为两类:

  1. 编程式事务(⼿动写代码操作事务)

  2. 声明式事务(利⽤注解⾃动开启和提交事务)

首先,要进行之前mybatis的数据库操作,这里小编就不在进行演示了;

🚀2.1编程式事务

SpringBoot 内置了两个对象:

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

  2. 的TransactionDefinition 是事务的属性, 在获取事务的时候需要将

TransactionDefinition 传递进去从⽽获得⼀个事务 TransactionStatus

具体的代码如下所示:

java 复制代码
@RestController
@RequestMapping("/user")
public class UserController {
    //手动改开启事务
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;
    @Autowired
    private TransactionDefinition transactionDefinition;

    @Autowired
    private UserService userService;

    @RequestMapping("/regist")
    public boolean regist(String userName,String password){

        //开启事务
        TransactionStatus transactionStatus=
                dataSourceTransactionManager.getTransaction(transactionDefinition);
        //操作数据库
        userService.registUser(userName,password);
        //提交事务
        dataSourceTransactionManager.commit(transactionStatus);

        //回滚事务
        dataSourceTransactionManager.rollback(transactionStatus);
        return true;
    }
}

解释:

注意这里要注入事务管理器对象,以及事务属性对象,这两个相当于是获取事务的状态,然后通过事务管理器通过提交或者回滚来操作我们的事务状态;

操作完数据库后,才进行事务的对应的处理哦~~~

具体的日志打印如下:

此时就是进行了事务的回滚,如果进行了事务的提交,那么就会存在一个commit单词的日志;

这里就是进行了事务的提交;

当然这里小编演示的时候,需要注释掉其中一个事务的操作,因为不可能事务提交又进行了回滚是吧~~~

🚀2.2声明式事务

这里的操作分为两步,主要式通过注解进行事务的自动开启:

第一步:添加依赖

XML 复制代码
<dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-tx</artifactId>
</dependency>

但是这里的依赖添加,这里小编添加了lombok,spring web,mybatis farmwork,mysql driver,并没有引入这里的依赖;

第二步:添加注解

在需要事务的⽅法上添加 @Transactional 注解就可以实现了. ⽆需⼿动开启事务和提交事

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

回滚事务

代码如下所示:

java 复制代码
@RestController
@RequestMapping("/trans")
public class TransController {
    @Autowired
    private UserService userService;
   
    @Transactional 
    @RequestMapping("t1")
    public Boolean register(String userName, String password){
        Integer result = userService.registUser(userName, password);
        System.out.println("插入用户表, result: "+ result);
     
        return true;
    }

当然,如果这里的存在异常情况下时,代码如下所示:

java 复制代码
@Transactional
    @RequestMapping("t1")
    public Boolean register(String userName, String password){
        Integer result = userService.registUser(userName, password);
        System.out.println("插入用户表, result: "+ result);
        
        int a = 10/0;
       
        return true;
    }

那么此时就会发生回滚,数据库就不会插入对应的数据;

但是对于所有发生了异常,此时事务都会进行回滚吗?当然不是,还要分为以下情况

1.异常捕获

当我们进行异常捕获后,代码如下所示

java 复制代码
@RestController
@RequestMapping("/trans")
public class TransController {
    @Autowired
    private UserService userService;
    /**
    程序异常,捕获并处理,这里结果就是提交
     */
    @Transactional
    @RequestMapping("t1")
    public Boolean register(String userName, String password){
        Integer result = userService.registUser(userName, password);
        System.out.println("插入用户表, result: "+ result);
        try {
            int a = 10/0;
        }catch (Exception e){
            //打印堆栈信息
            e.printStackTrace();
        }
        return true;
    }

解释:

这里就是通过try catch后进行堆栈信息的打印,所以最后就会进行事务的提交

(事务的提交与否,主要查看日志,是否存在commit的单词,或者去观察在数据库中数据是否进行了插入的操作)

2.异常捕获再次抛出

代码如下所示:

java 复制代码
 /**
    捕获异常之后,没有处理,直接再次抛出,回滚
     */
    @Transactional
    @RequestMapping("t2")
    public Boolean register1(String userName, String password){
        Integer result = userService.registUser(userName, password);
        System.out.println("插入用户表, result: "+ result);
        try {
            int a = 10/0;
        }catch (Exception e){
            throw e;
        }
        return true;
    }

解释:

在捕获到这里的算数异常后,再次把异常进行抛出,那么此时就相当于是没有进行捕获的,那么事务就会进行回滚,并且这里还可以进行手动回滚;

在捕获异常后,添加:

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

此时就会进行手动的回滚操作;

3.编译时异常

代码如下所示:

java 复制代码
/**
     *  事务提交
     */
    @Transactional
    @RequestMapping("/t4")
    public Boolean r4(String userName, String password) throws IOException {
        Integer result = userService.registUser(userName, password);
        System.out.println("插入用户表, result: "+ result);
        if (true){
            throw new IOException();//为编译时异常
        }

        return true;
    }

解释:

在事务的提交中,只有运行时异常,以及rerror会触发回滚,但是在这里的编译时异常是不可以回滚的(大致的可以理解为就是编译时异常有明显的报错,希望我们自己改正,这里事务就不管了)

当然还有一种情况

java 复制代码
/**
     *  事务回滚
     */
    @Transactional
    @SneakyThrows
    @RequestMapping("/t5")
    public Boolean r5(String userName, String password)  {
        Integer result = userService.registUser(userName, password);
        System.out.println("插入用户表, result: "+ result);
        if (true){
            throw new IOException();
        }
        //在反编译中这里会捕获异常,并再次抛出
        return true;
    }

这里的事务结果就是回滚,为什么呢?这里添加了注解@SneakyThrows,我们在反编译文件中可以看到这里还进行了异常的捕获,但是没有处理,直接把异常抛出了,所以这里就是第二种情况

4.运行时异常

代码如下所示:

java 复制代码
/**
     *  事务回滚
     */
    @Transactional
    @RequestMapping("/t6")
    public Boolean r6(String userName, String password)  {
        Integer result = userService.registUser(userName, password);
        System.out.println("插入用户表, result: "+ result);
        if (true){
            //这里抛出的就是运行时异常,需要进行回滚
            throw new RuntimeException();
        }

        return true;
    }

这里的的事务那么就是直接回滚了,小编不再进行过多的赘述;

5@transaction属性
  1. rollbackFor: 异常回滚属性. 指定能够触发事务回滚的异常类型. 可以指定多个异常类型

  2. Isolation: 事务的隔离级别. 默认值为 Isolation.DEFAULT

  3. propagation: 事务的传播机制. 默认值为 Propagation.REQUIRED

注意:

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

Exception的⼦类中, 除了RuntimeException及其⼦类其他的会不进行回滚

但是我们可以指定这里rollbackfor为所有异常类型,所以对于所有的异常,此时都会进行回滚的操作;代码如下所示:

java 复制代码
@Transactional(rollbackFor = Exception.class, isolation = Isolation.DEFAULT)
    //设置所有回滚异常类型
    @RequestMapping("/r7")
    public Boolean r7(String userName, String password) throws IOException {
        Integer result = userService.registUser(userName, password);
        System.out.println("插入用户表, result: "+ result);
        if (true){
            throw new IOException();
        }

        return true;
    }
6.@transaction总结

@Transactional 可以⽤来修饰⽅法或类:

• 修饰⽅法时: 只有修饰public ⽅法时才⽣效(修饰其他⽅法时不会报错, 也不⽣效)[推荐]

• 修饰类时: 对 @Transactional 修饰的类中所有的 public ⽅法都⽣效.

⽅法/类被 @Transactional 注解修饰时, 在⽬标⽅法执⾏开始之前, 会⾃动开启事务, ⽅法执⾏结束

之后, ⾃动提交事务.

如果在⽅法执⾏过程中, 出现异常, 且异常未被捕获, 就进⾏事务回滚操作.

如果异常被程序捕获, ⽅法就被认为是成功执⾏, 依然会提交事务,但是捕获后重新抛出,就会回滚,若为运行时异常,那么也会进行回滚,但是编译时异常在不添加SneakyThrow时为提交;

📚️3.总结

本期主要讲解了关于事务,以及Spring事务的知识,对于Spring 事务的两种方式,小编都进行了代码的演示;

🌅🌅🌅~~~~最后希望与诸君共勉,共同进步!!!


💪💪💪以上就是本期内容了, 感兴趣的话,就关注小编吧。

😊😊 期待你的关注~~~

相关推荐
昔我往昔4 分钟前
Spring Boot中的扫描注解如何使用
java·spring boot·后端
造梦师阿鹏13 分钟前
【SpringBoot】@Value 没有注入预期的值
java·spring boot·后端·spring
jimiStephen30 分钟前
Mybatis原理简介
java·mybatis
qingy_20461 小时前
【JavaWeb】JavaWeb入门之Tomcat详解
java·tomcat
胡图蛋.1 小时前
什么是MVCC
java·服务器·数据库
V+zmm101341 小时前
新冠肺炎服务预约微信小程序的设计与实现ssm+论文源码调试讲解
java·数据库·微信小程序·小程序·毕业设计
Charlie__ZS2 小时前
Docker基础
java·docker
孙尚香蕉2 小时前
Hadoop高可用集群搭建
java·linux·hadoop
啊松同学2 小时前
【Java】设计模式——代理模式
java·后端·设计模式·代理模式
爱上语文2 小时前
预编译SQL
java·数据库·后端·sql