【Spring Boot 事务】万字详解Spring Boot 事务,赶快跟随良辰一起去学习Spring Boot 事务吧! ! !

news2024/11/24 16:48:19

前言:
大家好,我是良辰丫,这篇文章我将带领大家一起去学习Spring Boot 事务文章,我们在学习数据库的时候已经接触过事务了,来跟随我的脚步一起来瞧一下Spring Boot 事务吧.💌💌💌

🧑个人主页:良辰针不戳
📖所属专栏:javaEE进阶篇之框架学习
🍎励志语句:生活也许会让我们遍体鳞伤,但最终这些伤口会成为我们一辈子的财富。
💦期待大家三连,关注,点赞,收藏。
💌作者能力有限,可能也会出错,欢迎大家指正。
💞愿与君为伴,共探Java汪洋大海。

在这里插入图片描述

目录

  • 1. 回忆事务
    • 1.1 事务是什么
    • 1.2 数据库中的事务
  • 2. Spring Boot 事务
    • 2.1 简单回忆一下SSM项目交互过程
    • 2.2 编程式事务
    • 2.3 声明式事务
      • 2.3.1 声明式事务提交
      • 2.3.2 声明式事务回滚
      • 2.3.3 try catch处理异常
  • 3. 注解 @Transactional的参数
  • 4. 注解 @Transactional的工作原理
  • 4. 事务的隔离级别
    • 4.1 事务的四大特性
    • 4.2 设置事务隔离级别的原因
    • 4.3 如何设置事务隔离级别
    • 4.4 数据库的事务隔离级别
    • 4.5 Spring 事务隔离级别

1. 回忆事务

1.1 事务是什么

  • 事务是指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败.举一个简单的转账例子,张三给李四转账100元,此时张三的余额减去100,李四的余额加上100,这样财富和我们的预期.
  • 在我们学习java后端的过程中,事务的学习是必不可少的,因为我们要做项目,我们可以这样理解事务就是处理一些项目中的逻辑问题.

1.2 数据库中的事务

在数据库中我们就接触了事务,在事务中常见的操作如下.

-- 开启事务
start transaction;

-- 提交事务
commit;

-- 回滚事务
rollback;

2. Spring Boot 事务

  • 今天的学习会依赖咱们的mybatis的学习,如果大家还是对之前知识有疑惑的可以看我的mybatis文章.
  • 链接: MyBatis学习

2.1 简单回忆一下SSM项目交互过程

我们在MyBatis已经带大家了解了SSM项目交互过程,今天我带大家回忆一下,为什么多次说呢?当然因为是核心(重要点),明白了各个层次如何交互,才能够真正去了解SSM代码的架构.

在这里插入图片描述

  • 客户通过浏览器进行访问页面,也就是客户通过浏览器向服务器发送请求.
  • 此时是通过控制器接收请求,收到请求后,控制器调用服务层.
  • 服务层收到控制器的调用请求后,服务层调用持久层.
  • 持久层收到服务层的调用请求后,连接数据(也就是操作数据库,与数据库建立连接).
  • 此时数据库向持久层返回数据,持久层向服务层返回数据,服务器又向控制器返回数据,控制器最终把数据返回给浏览器(也就是前端页面).

2.2 编程式事务

所谓编程式事务,是完全通过代码来实现事务的逻辑,我们只需要简单了解.

  1. 我们先创一个SSM项目框架.

在这里插入图片描述

  1. 在yml配置文件中做相应的配置
# 数据库连接配置
spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/student?characterEncoding=utf8
    username: root
    password: "123456"
    driver-class-name: com.mysql.cj.jdbc.Driver
# 设置 Mybatis 的 xml 保存路径
mybatis:
  mapper-locations: classpath:mapper/*Mapper.xml
  configuration: # 配置打印 MyBatis 执行的 SQL
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 配置打印 MyBatis 执行的 SQL日志级别
logging:
  level:
    com:
      example:
        demo: debug
  1. 创建实体类
package com.example.demo.entity;

import lombok.Data;

@Data
public class Stu {
    private Integer id;
    private String name;
    private Integer age;
}

  1. 创建service层
package com.example.demo.service;

import com.example.demo.entity.Stu;
import com.example.demo.mapper.StuMapper;
import org.springframework.beans.factory.annotation.Autowired;

public class StuService {
    @Autowired
    private StuMapper stuMapper;
    public Integer add(Stu stu){
        return stuMapper.add(stu);
    }
}

  1. 创建controller层次
package com.example.demo.controller;

import com.example.demo.entity.Stu;
import com.example.demo.service.StuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/stu")
public class StuController {
    @Autowired
    private StuService stuService;

    //注入事务的对象
    @Autowired
    private DataSourceTransactionManager transactionManager;

    //设置事务的属性
    @Autowired
    private TransactionDefinition transactionDefinition;

    @RequestMapping("/add")
    public int add(Stu stu){
        //非空校验
        if (stu == null || stu.getId() ==null
                || stu.getName() == null
                || stu.getName() == null) {
            return 0;
        }
        //1.开启事务(下面语句表示得到并开启事务)
        TransactionStatus transactionStatus =
                transactionManager.getTransaction(transactionDefinition);

        int res = stuService.add(stu);
        System.out.println(res);

        //2.回滚事务
        transactionManager.rollback(transactionStatus);
        return res;
    }
}

  1. 通过postman进行测试

在这里插入图片描述

在这里插入图片描述

接下来我们发现数据库中数据是空的,因为我们进行了回滚操作.

在这里插入图片描述

  1. 提交事务

接下来我们不进行回滚,写一个提交事务的代码.

package com.example.demo.controller;

import com.example.demo.entity.Stu;
import com.example.demo.service.StuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/stu")
public class StuController {
    @Autowired
    private StuService stuService;

    //注入事务的对象(通过事务管理器拿到事务)
    @Autowired
    private DataSourceTransactionManager transactionManager;

    //设置事务的属性(事务的定义对象,拿一个事务的时候设置相应的属性)
    @Autowired
    private TransactionDefinition transactionDefinition;

    @RequestMapping("/add")
    public int add(Stu stu){
        //非空校验
        if (stu == null || stu.getId() ==null
                || stu.getName() == null
                || stu.getName() == null) {
            return 0;
        }
        //1.开启事务(下面语句表示得到并开启事务)
        TransactionStatus transactionStatus =
                transactionManager.getTransaction(transactionDefinition);

        int res = stuService.add(stu);
        System.out.println(res);

//        //2.回滚事务
//        transactionManager.rollback(transactionStatus);
        //提交事务
        transactionManager.commit(transactionStatus);
        return res;

    }
}

此时我们的数据库就有数据了.

在这里插入图片描述

2.3 声明式事务

  • 编程式事务相当于是手动挡,那么我们的声明式事务就相当于自动挡了(进入智能时代了).
  • 接下来我们需要认识我们的事务注解 @Transactional,它可以自动提交事务.

@Transactional的优点:

  • 可以添加在类上或者方法上,如果添加在类上,那么该类中的所有方法都具有事务的特点.
  • 在方法执行前自动开启事务,在方法执行完(在没有异常的情况下)自动提交事务;当在方法执行期间出现异常的时候就会自动回滚事务.

2.3.1 声明式事务提交

   //声明式的自动提交事务
    @Transactional
    @RequestMapping("/add2")
    public int add2(Stu stu){
        //非空校验
        if (stu == null || stu.getId() ==null
                || stu.getName() == null
                || stu.getName() == null) {
            return 0;
        }

        int res = stuService.add(stu);
        return res;
    }

可以看出声明式事务几行代码就帮我们搞定了需求,非常方便.

在这里插入图片描述

在这里插入图片描述

接下来我们看一下数据库,我们会发现李四的数据已经在数据库里面了.

在这里插入图片描述

2.3.2 声明式事务回滚

接下来我们制造一个异常,让它自动进行回滚操作.

    //声明式的自动提交事务
    @Transactional
    @RequestMapping("/add2")
    public int add2(Stu stu){
        //非空校验
        if (stu == null || stu.getId() ==null
                || stu.getName() == null
                || stu.getName() == null) {
            return 0;
        }

        int res = stuService.add(stu);
        int num = 100/0;
        return res;
    }

此时我们的访问页面就变成了这样,因为前端感知到了异常.

在这里插入图片描述

在这里插入图片描述

显示添加成功,然后我们看数据库

在这里插入图片描述

这里却没有王五的信息,可见回滚成功.
到了这里,大家可能会感到是异常干扰了数据库的执行???
那么我们注释掉提交事务的注解,再次执行观察效果.

在这里插入图片描述

页面还会出现异常信息.

在这里插入图片描述

在这里插入图片描述

因为我们没有加事务的注解,遇到异常不会回滚

2.3.3 try catch处理异常

try catch处理异常后即使有异常也不会进行回滚,因为这样做程序感知不到异常,程序只相信自己感知的异常,如果它感知不到异常,它就认为没有异常.

@Transactional
    @RequestMapping("/add2")
    public int add2(Stu stu){
        //非空校验
        if (stu == null || stu.getId() ==null
                || stu.getName() == null
                || stu.getName() == null) {
            return 0;
        }

        int res = stuService.add(stu);
        try {
            int num = 100/0;
        } catch (Exception e) {
            e.printStackTrace();
        } 
        return res;
    }

此时我们的浏览器页面并没有报异常.

在这里插入图片描述

然后我们来看我们的数据库信息,我们会发现并没有回滚操作.

在这里插入图片描述
那么我们如何让它在try-catch的处理后还能进行回滚呢?

  1. 将异常抛出,在catch中
   @Transactional
    @RequestMapping("/add2")
    public int add2(Stu stu){
        //非空校验
        if (stu == null || stu.getId() ==null
                || stu.getName() == null
                || stu.getName() == null) {
            return 0;
        }

        int res = stuService.add(stu);
        try {
            int num = 100/0;
        } catch (Exception e) {
            throw e;
//            e.printStackTrace();
        }
        return res;
    }

在这里插入图片描述

在这里插入图片描述
但是这种方式并不优雅,有点暴力,浏览器页面也不美观.

  1. 使用代码手动回滚异常
    @Transactional
    @RequestMapping("/add2")
    public int add2(Stu stu){
        //非空校验
        if (stu == null || stu.getId() ==null
                || stu.getName() == null
                || stu.getName() == null) {
            return 0;
        }

        int res = stuService.add(stu);
        try {
            int num = 100/0;
        } catch (Exception e) {
//            throw e;
//            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return res;
    }

在这里插入图片描述

在这里插入图片描述

这种方式是比较推荐的.

3. 注解 @Transactional的参数

参数作用
value当配置了多个事务管理器后,可以使用该属性指定选择哪个事务管理器
transactionManager当配置了多个事务管理器后,可以使用该属性指定选择哪个事务管理器
propagation表示事务的传播行为,默认为Propagation.REQUIRED
isolation事务的隔离级别,默认值为Isolation.DEFAULT
timeout事务的超出时间,默认值为-1(没有超出时间),如果超过该时间限制但事务没有完成,那么自动回滚事务(一般不建议大家使用这个)
readOnly指定事务是否为只读事务,默认值为false.为了忽略那些不需要事务的方法,不如读取数据,可以设置readOnly为true.
rollbackFor用于指定能够触发事务回滚的异常类型,可以指定多个异常类型.
rollbackForClassName用于指定能够触发事务回滚的异常类型,可以指定多个异常类型.
norollbackFor抛出指定的异常类型,不会滚事务,也可以指定多个异常类型.
norollbackForClassName抛出指定的异常类型,不会滚事务,也可以指定多个异常类型.

4. 注解 @Transactional的工作原理

  • @Transactional 是基于 AOP 实现的,AOP ⼜是使⽤动态代理实现的。如果⽬标对象实现了接⼝,默认情况下会采⽤ JDK 的动态代理,如果⽬标对象没有实现了接⼝,会使⽤ CGLIB 动态代理。
  • @Transactional 在开始执⾏业务之前,通过代理先开启事务,在执⾏成功之后再提交事务。如果中途遇到的异常,则回滚事务。
  • @Transactional 实现思路如下,其中开启事务与提交事务(或者回滚事务)依赖于切面.
    在这里插入图片描述
  • @Transactional执行过程我们简单了解即可.

4. 事务的隔离级别

4.1 事务的四大特性

  • 原⼦性(Atomicity,或称不可分割性):⼀个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执⾏过程中发⽣错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执⾏过⼀样。

  • ⼀致性(Consistency):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写⼊的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以⾃发性地完成预定的⼯作。

  • 持久性(Durability) : 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

  • 隔离性(Isolation,⼜称独⽴性) : 数据库允许多个并发事务同时对其数据进⾏读写和修改的能⼒,隔离性可以防⽌多个事务并发执⾏时由于交叉执⾏⽽导致数据的不⼀致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串⾏化(Serializable)。

4.2 设置事务隔离级别的原因

  • 上面的四种特性中,只有隔离性(隔离级别)是可以设置的
  • 设置事务的隔离级别是⽤来保障多个并发事务执⾏更可控,更符合操预期的
  • 怎么理解呢,假设有一个平台,张三申请的账号与李四申请的账号资源要进行隔离

4.3 如何设置事务隔离级别

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

在这里插入图片描述

4.4 数据库的事务隔离级别

  • READ UNCOMMITTED:读未提交,也叫未提交读,该隔离级别的事务可以看到其他事务中未提交的数据。该隔离级别因为可以读取到其他事务中未提交的数据,⽽未提交的数据可能会发⽣回滚,因此我们把该级别读取到的数据称之为脏数据,把这个问题称之为脏读。
  • READ COMMITTED:读已提交,也叫提交读,该隔离级别的事务能读取到已经提交事务的数据,因此它不会有脏读问题。但由于在事务的执⾏中可以读取到其他事务提交的结果,所以在不同时间的相同 SQL 查询中,可能会得到不同的结果,这种现象叫做不可重复读。
  • REPEATABLE READ:可重复读,是 MySQL 的默认事务隔离级别,它能确保同⼀事务多次查询的结果⼀致。但也会有新的问题,⽐如此级别的事务正在执⾏时,另⼀个事务成功的插⼊了某条数据,但因为它每次查询的结果都是⼀样的,所以会导致查询不到这条数据,⾃⼰重复插⼊时⼜失败(因为唯⼀约束的原因)。明明在事务中查询不到这条信息,但⾃⼰就是插⼊不进去,这就叫幻读(Phantom Read)。
  • SERIALIZABLE:序列化,事务最⾼隔离级别,它会强制事务排序,使之不会发⽣冲突,从⽽解决了脏读、不可重复读和幻读问题,但因为执⾏效率低,所以真正使⽤的场景并不多.
事务隔离级别脏读不可重复读幻读
读未提交
读已提交×
可重复读××
串行化×××
  • 脏读:⼀个事务读取到了另⼀个事务修改的数据之后,后⼀个事务⼜进⾏了回滚操作,从⽽导致第⼀个事务读取的数据是错误的。
  • 不可重复读:⼀个事务两次查询得到的结果不同,因为在两次查询中间,有另⼀个事务把数据修改了。
  • 幻读:⼀个事务两次查询中得到的结果集不同,因为在两次查询中另⼀个事务有新增了⼀部分数据。

4.5 Spring 事务隔离级别

  • Isolation.DEFAULT:以连接的数据库的事务隔离级别为主。
  • Isolation.READ_UNCOMMITTED:读未提交,可以读取到未提交的事务,存在脏读。
  • Isolation.READ_COMMITTED:读已提交,只能读取到已经提交的事务,解决了脏读,存在不可重
    复读。
  • Isolation.REPEATABLE_READ:可重复读,解决了不可重复读,但存在幻读(MySQL默认级
    别)。
  • Isolation.SERIALIZABLE:串⾏化,可以解决所有并发问题,但性能太低

从上述介绍可以看出,相⽐于 MySQL 的事务隔离级别,Spring 的事务隔离级别只是多了⼀个

Isolation.DEFAULT(以数据库的全局事务隔离级别为主)。
Spring 中事务隔离级别只需要设置 @Transactional ⾥的 isolation 属性即可.

@RequestMapping("/stu")
@Transactional(isolation = Isolation.SERIALIZABLE)
public Object save(User user) {
 // 业务代码
}

后序 :
到了这里关于Spring Boot 事务的学习也就结束了,下一篇文章我将带领大家去学习Spring 事务传播机制.🍬🍬🍬

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/716588.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

得物社区推荐精排模型演进

1.背景 得物社区是一大批年轻人获取潮流信息、分享日常生活的潮流生活社区。其中用户浏览的信息,进行个性化的分发,是由推荐系统来决策完成的。目前得物社区多个场景接入了推荐算法,包括首页推荐双列流、沉浸式视频推荐、分类tab推荐流、直播…

Tomcat的安装部署及优化

tomcat安装部署 tomcat安装部署 一、概述:1.tomcat介绍:2.tomcat核心组件:3.tomcat处理内部请求(数据流向图)4.tomcat服务部署: 二、虚拟主机配置:1.创建 chan 和 baek 项目目录和文件&#xf…

Python常见的开发工具合集对比

​ Python是一种功能强大且易于学习的编程语言,被广泛应用于数据科学、机器学习、Web开发等领域。随着Python在各个领域的应用越来越广泛,越来越多的Python开发工具也涌现出来。但是,对于新手来说,选择一款合适的Python开发工具可…

BurpSutie拓展插件推荐-漏洞扫描插件

为方便您的阅读,可点击下方蓝色字体,进行跳转↓↓↓ 01 fastjsonScan(1)工具介绍(2)下载地址(3)使用说明 02 SpringScan(1)工具介绍(2&#xff09…

python接口自动化(十三)--cookie绕过验证码登录(详解)

简介 有些登录的接口会有验证码:短信验证码,图形验证码等,这种登录的话验证码参数可以从后台获取的(或者查数据库最直接)。获取不到也没关系,可以通过添加cookie的方式绕过验证码。(注意&#x…

GPT模型训练实践(1)-基础概念

GPT 是 Generative Pre-trained Transformers 的缩写,一种先进的深度学习模型,旨在生成类人文本。GPT 的三个组成部分Generative、Pre-trained 和 Transformer,其解释如下: Generative生成:生成模型是用于生成新数据的…

【DASOU视频记录】Transformer从零详细解读

文章目录 来源transformer的全局理解位置编码多头注意力机制残差Batch NormalLayer NormalDecoder 来源 b站链接 transformer的全局理解 输入中文,输出英文 细化容易理解的结构,就是先编码,再解码 进一步细化的结构,就是多个编…

Python_闭包

目录 1.概念介绍 2.闭包初探 3.闭包陷阱 4.闭包的应用 4.1 潜在的问题 5.闭包的实现 闭包并不只是一个python中的概念,在函数式编程语言中应用较为广泛。理解python中的闭包一方面是能够正确的使用闭包,另一方面可以好好体会和思考闭包的设计思想。…

面试又问到:工作中发现的最有价值的bug?答不好offer要飞了...

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 这个问题&#xf…

SpringBoot--超时熔断器

需求背景 如果一个服务中有很多涉及需要服务间熔断的地方,就会出现N多下述代码: 1.N个fegnClient接口 FeignClient(name "hello-world-service", fallback HelloWorldFallback.class) public interface HelloWorldService {GetMapping(&q…

gma 2 教程(一)概述:3. 探索 GMA

组织方式 gma 整体按照库-模块-类/函数-(方法/属性/子类)的思路构建,详细思路如下所示: 整体架构 gma内主要模块与功能对应关系见下表: 模块名中文名对应主要功能io输入输出栅格/矢量数据输入输出模块crs坐标系统坐…

vim的使用方法及相关按键

目录 一、安装vim 二、vim的使用 1.打开vim 2.vim的四种模式使用 (1)命令模式(快捷键的使用) (2)编辑模式 (3)末行模式 (4)可视化模式 一、安装vim …

022:vue中tree结构数据变成扁平化table结构数据的示例

第022个 查看专栏目录: VUE — element UI vue在使用element UI tree的时候,有的时候是要做逆向处理的,即将树形结构的数据转化为table结构的数据,即扁平化的json数据。 如何处理呢? 效果图 原始tree结构数据: let newdata= [ {

redis pipeline

redis 执行多条连续的命令的时候为了减少网络开销RTT,可以使用pipeline技术。 pipeline 与 原生批命令(mset, mget) 对比: 原生批命令是原子性,pipeline是非原子性 (原子性概念:一个事务是一个不可分割的最小工作单位,要么都成功要么都失败…

基于Anime2Sketch算法那将图片转成素描

1.下载源码地址 https://github.com/Mukosame/Anime2Sketch下载项目依赖包,下载模型权重文件 运行看效果 python test.py --datarootE:\01_hjz\datas\00-hjz\pictures --load_size512调整自定义测试图片路径 """Test script for anime-to-sketch…

基于Java+Vue前后端分离网上书城系统设计实现(源码+lw+部署文档+讲解等)

博主介绍:✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专…

ChatGPT+低代码,好用到飞起?

ChatGPT 凭借短短 2 个月,月活用户突破 1 亿,成为史上用户增长速度最快的消费级应用程序。ChatGPT 的爆火,在全球范围内掀起了一场关于 AI 技术革命的狂潮,AIGC 也迅速成为科技圈最火赛道。 更有国际咨询机构预测,203…

IIS安装配置和简单网站部署流程

IIS安装和网站配置 环境:win10 注意:这是在win10下部署iis,开发环境下部署,开发测试,非windows server IIS简介 Internet Information Services,简称IIS,是微软提供基于windows的互联网信息服务&#x…

微信小程序使用第三方组件wxParse加载富文本html

微信小程序使用第三方组件wxParse加载富文本html 微信小程序微信小程序加载富文本html微信小程序富文本第三方组件wxParsewxParse富文本html wxParse简介 wxParse 是一个微信小程序富文本解析组件,支持支持Html及markdown转wxml。 wxParse gitHub地址&#xff1…

亿发软件:玩具制造行业批发ERP系统解决方案,赋能传统制造商数字化

我国长期以来被公认为玩具制造大国,受益于其制造基础设施和成本优势。此外,可支配收入的增加和用户生活方式的改变增加了国内外对玩具的需求。然而,行业也面临着挑战和转型。随着数字技术的出现和用户偏好的变化,玩具ERP系统在确保…