【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 事务的两种方式,小编都进行了代码的演示;

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


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

😊😊 期待你的关注~~~

相关推荐
xiaoye370820 小时前
Spring 中高级面试题
spring·面试
枫叶落雨22221 小时前
ShardingSphere 介绍
java
花花鱼21 小时前
Spring Security 与 Spring MVC
java·spring·mvc
言慢行善21 小时前
sqlserver模糊查询问题
java·数据库·sqlserver
专吃海绵宝宝菠萝屋的派大星1 天前
使用Dify对接自己开发的mcp
java·服务器·前端
大数据新鸟1 天前
操作系统之虚拟内存
java·服务器·网络
Tong Z1 天前
常见的限流算法和实现原理
java·开发语言
凭君语未可1 天前
Java 中的实现类是什么
java·开发语言
He少年1 天前
【基础知识、Skill、Rules和MCP案例介绍】
java·前端·python
克里斯蒂亚诺更新1 天前
myeclipse的pojie
java·ide·myeclipse