SpringBoot 事务

前情提要

飞书的文档不好转移,可以直接看我的飞书:Docs

什么是事务

是一组操作的集合,是一个不可分割的操作

事物会将所有操作当作一个整体,同时对数据库进行操作请求,这些操作要么全部成功,要么全部失败

总会有一些操作,需要同步进行,这个时候就需要使用事务

数据库中,自增类型的字段,一旦被分配了数据,就是不会进行回滚的,尽管整个数据进行了回滚(或者数据没有成功进行添加),但是该字段还是进行了自增

操作步骤
  1. 开启事 start transaction/begin(操作前开启事务)

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

  3. 回滚事务: rollback(操作中只要有一个出现异常,就回滚事务)

    – 开启事务
    start transaction;

    – 提交任务
    commit;

    – 回滚事务
    rollback;

事务的实现

分类(Spring):

  1. 编程式事务(手动代码)

  2. 声明式事务(利用注解自动)

编程式事务(了解)
添加依赖
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
</dependency>

定义对象.使用@Autowired,都是Spring帮忙处理过的

  1. DataSourceTransactionManager 生成的事务管理器

  2. TransactionDefinition 需要注入的对象

对应的start操作:

TransactionStatus transactionStatus = DataSourceTransactionManager.getTransaction(transactionDefinition);

回滚

DataSourceTransactionManager.rollback(transactionStatus);

代码示例
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private DataSourceTransactionManager TransactionManager;//事务管理器
    @Autowired
    private TransactionDefinition transactionDefinition;//需要注入的参数
    @Autowired
    private UserService userService;
    @RequestMapping("/registry")
    public Boolean registry(String userName,String password){
        TransactionStatus transactionStatus = TransactionManager.getTransaction(transactionDefinition);//设定了开始状态
        Integer result = userService.insertInfo(userName,password);
        log.info("插入信息: "+result);
        TransactionManager.rollback(transactionStatus);//回滚
        return true;
    }
}


@Service
public class UserService {
    @Autowired
    private UserInfoMapper userInfoMapper;
    public Integer insertInfo(String userName, String password) {
        return userInfoMapper.insertInfo(userName,password);
    }
}


@Mapper
public interface UserInfoMapper {
    @Insert("insert into user_info(`user_name`,`password`) values (#{userName},#{password})")
    Integer insertInfo(@Param("userName") String userName, @Param("password") String password);
}
声明式事务 @Transactional (主要使用方法!!!)

对于 @Transactional 注解,可以使用在类上(对类中的方法均生效),也可以使用在方法上(只对该方法生效)

加入注解之后,会自动开启事务,自动判断执行commit或者rollback操作,但是这个注解只会对 public 方法生效

代码类型
  1. 提交成功

    @Transactional
    @RequestMapping(“/registry”)
    public Boolean registry(String userName,String password){
    Integer result = userService.insertInfo(userName,password);
    log.info(“影响的行数:”+result);
    return true;
    }

2.出现异常,回滚

没有出现 commit 语句

@Transactional
@RequestMapping("/registry2")
public Boolean registry2(String userName,String password){
    Integer result = userService.insertInfo(userName,password);
    log.info("影响的行数:"+result);
    int i = 10/0;
    return true;
}

3.捕获异常

将异常进行捕获之后,就会判断没有错误,此时就会正常进行事务提交

@Transactional
@RequestMapping("/registry3")
public Boolean registry3(String userName,String password){
    Integer result = userService.insertInfo(userName,password);
    log.info("影响的行数:"+result);
    try {
        int i = 10/0;
    }catch (Exception e){
        log.info("出现异常");
    }
    return true;
}

4.捕获异常后继续抛出

@Transactional
@RequestMapping("/registry4")
public Boolean registry4(String userName,String password){
    Integer result = userService.insertInfo(userName,password);
    log.info("影响的行数:"+result);
    try {
        int i = 10/0;
    }catch (Exception e){
        log.info("出现异常");
        throw e;
    }
    return true;
}

5.手动设置回滚

手动设置之后,代码不会报错,但是还是会进行回滚

手动设置回滚代码: TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()

@Transactional
@RequestMapping("/registry5")
public Boolean registry5(String userName,String password){
    Integer result = userService.insertInfo(userName,password);
    log.info("影响的行数:"+result);
    try {
        int i = 10/0;
    }catch (Exception e){
        log.info("设置手动回滚");
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
    return true;
}

6.手动设置其他异常回滚

在注解进行回滚类型修改,当抛出异常时,会将该异常当作设置异常进行处理,同时进行回滚,这样就避免了注解不会回滚其他类型异常的问题

@Transactional(rollbackFor = Exception.class,isolation = Isolation.DEFAULT)
@RequestMapping("/registry6")
public Boolean registry6(String userName,String password) throws IOException {
    Integer result = userService.insertInfo(userName,password);
    log.info("影响的行数:"+result);
    if(true){
        throw new IOException();
    }
    return true;
}

回滚类型:
  1. 运行时异常(其他类型的异常是不会回滚的)–>RuntimeException

  2. ERROR

其他异常的回滚需要进行单独设置

异常处理

对于异常进行捕获,没有抛出(内部解决),此时会认为没有报错,还是会提交!!

但是捕获后,重新抛出,还是会回滚

同时,捕获后可以设置手动回滚,此时事务还是会回滚

事务的隔离级别

级别分类

读未提交

该隔离级别的事务可以看到其他事务中未提交的数据,将他发生的问题叫做脏读(还没有进行提交,其他事务就已经读取到了,但是可能数据会回滚)

读提交

该隔离级别的事务可以读取到已经提交事务的数据(在不同的时间的相同SQL查询可能会有不同的结果) 问题:不可重复度

可重复读

不会读到其他事务对已有数据的修改,确保同一事务多次查询的结果是一致的,MySQL的默认事务隔离级别

串行化

序列化,事务最高隔离级别,会强制事务排序,不会脏读,不可重复读和幻读,但是效率低

图解(AB都是同一个表)

读已提交(不可重复读)

可重复读

事务传播机制!(MsSQL里面不涉及,但是Java涉及)

场景一:当方法A设置了事务注解的同时,还调用了方法B,但是方法B也是设置了事务注解,此时如果回滚,是按照方法A还是方法B呢 --> 当B发生异常需要回滚,此时A需要回滚吗

场景二:当方法A设置了事务注解的同时,还调用了方法B,但是方法B也是设置了事务注解,此时方法B是加入A的事务还是创建新事务呢

传播机制!

解析(全部建立在B设定了**@Transactional(propagation=)**的前提下):

不创建新事务部分:

  1. 如果A有事务,B就加入,此时共用一个事务,二者之间只要任意一个出现问题,就全部回滚;如果A没有事务,B就创建事务(这个是默认的)

  2. 如果A有事务,B就加入;如果A没有事务,B就以没有事务的状态继续运行(此时就是普通调用)

  3. 如果A存在事务,B就加入;如果A没有,B就会抛出异常(强制性)

创建新事务部分:

  1. 不管A是否有事务,B都是会创建一个新事务

  2. 不管A是否有事务,B都还是以没有事务的状态运行

  3. 如果A有事务,B就抛出异常;如果A没有事务,B就以没有事务的状态继续运行

  4. 如果A有事务,B就会创建一个事务,作为A的事务的嵌套事务;如果A没有事务,B就会像第一条

场景演示
代码准备
  1. Controller

    @Slf4j
    @RestController
    @RequestMapping(“trans2”)
    public class LogController {

    @Autowired
    private UserService userService;
    @Autowired
    private LogService logService;
    @Transactional
    @RequestMapping("/registry")
    public Boolean registry(String userName,String password){
        Integer result = userService.insertInfo(userName,password);
        log.info("表一影响的行数:"+result);
        Integer result1 = logService.insertInfo(userName,"registry");
        log.info("表二影响的行数:"+result1);
        return true;
    }
    

    }

2.表1

@Service
public class UserService {
    @Autowired
    private UserInfoMapper userInfoMapper;
    public Integer insertInfo(String userName, String password) {
        return userInfoMapper.insertInfo(userName,password);
    }
}


@Mapper
public interface UserInfoMapper {
    @Insert("insert into user_info(`user_name`,`password`) values (#{userName},#{password})")
    Integer insertInfo(@Param("userName") String userName, @Param("password") String password);
}
  1. 表2

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

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

    }

    @Mapper
    public interface LogInfoMapper {
    @Insert(“insert into log_info(user_name,op) values (#{userName},#{op})”)
    Integer insertInfo(@Param(“userName”) String userName, @Param(“op”) String op);
    }

进行测试
  1. Propagation.REQUIRED

正常运行

代码(只显示修改部分)

@Transactional
@RequestMapping("/registry")
public Boolean registry(String userName,String password){
    Integer result = userService.insertInfo(userName,password);
    log.info("表一影响的行数:"+result);
    Integer result1 = logService.insertInfo(userName,"registry");
    log.info("表二影响的行数:"+result1);
    return true;
}

@Transactional(propagation = Propagation.REQUIRED)
public Integer insertInfo(String userName, String password) {
    return userInfoMapper.insertInfo(userName,password);
}

@Transactional(propagation = Propagation.REQUIRED)
public Integer insertInfo(String userName,String op){
    return logInfoMapper.insertInfo(userName,op);
}

2.Propagation.SUPPORTS

取消Controller的事务注解,其他被调用的方法以普通方式运行

代码

    @RequestMapping("/registry")
    public Boolean registry(String userName,String password){
        Integer result = userService.insertInfo(userName,password);
        log.info("表一影响的行数:"+result);
        Integer result1 = logService.insertInfo(userName,"registry");
        log.info("表二影响的行数:"+result1);
        return true;
    }
    
    
@Transactional(propagation = Propagation.SUPPORTS)
public Integer insertInfo(String userName, String password) {
    return userInfoMapper.insertInfo(userName,password);
}


@Transactional(propagation = Propagation.SUPPORTS)
public Integer insertInfo(String userName,String op){
    return logInfoMapper.insertInfo(userName,op);
}

3.Propagation.MANDATORY

取消Controller的事务注解,此时报错

代码

@Transactional(propagation = Propagation.MANDATORY)
public Integer insertInfo(String userName, String password) {
    return userInfoMapper.insertInfo(userName,password);
}

@Transactional(propagation = Propagation.MANDATORY)
public Integer insertInfo(String userName,String op){
    return logInfoMapper.insertInfo(userName,op);
}

4.Propagation.REQUIRES_NEW

取消Controller的事务注解,此时调用的方法创建新事务

代码

@Transactional(propagation = Propagation.REQUIRES_NEW)
public Integer insertInfo(String userName, String password) {
    return userInfoMapper.insertInfo(userName,password);
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public Integer insertInfo(String userName,String op){
    return logInfoMapper.insertInfo(userName,op);
}

5.Propagation.NOT_SUPPORTED

由Controller开启事务

代码

@Transactional
@RequestMapping("/registry")
public Boolean registry(String userName,String password){
    Integer result = userService.insertInfo(userName,password);
    log.info("表一影响的行数:"+result);
    Integer result1 = logService.insertInfo(userName,"registry");
    log.info("表二影响的行数:"+result1);
    return true;
}

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public Integer insertInfo(String userName, String password) {
    return userInfoMapper.insertInfo(userName,password);
}

6.Propagation.NEVER

Controler有事务

运行截图

Controller没有事务

运行截图

7. Propagation.NESTED

应该类似于连坐制,可以进行部分回滚

运行截图

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

汇川Easy系列正弦信号发生器(ST源代码)

正弦余弦信号发生器CODESYS和MATLAB实现请参考下面文章链接: 正弦余弦信号发生器应用(CODESYS ST源代码+MATLAB仿真)_st语言根据输入值,形成正弦点-CSDN博客文章浏览阅读410次。本文介绍了如何在CODESYS编程环境中创建正弦和余弦信号发生器。通过详细的PLC梯形图和SCL语言代码…

【JMeter详解】

JMeter详解 Apache JMeter 是一个开源的、100%纯Java应用程序&#xff0c;设计用于负载测试和性能测量。它最初是为测试Web应用程序而设计的&#xff0c;但后来扩展到其他测试功能。JMeter可以用来对静态和动态资源&#xff08;如静态文件、Servlets、Perl脚本、Java对象、数据…

uniapp:微信小程序文本长按无法出现复制菜单

一、问题描述 在集成腾讯TUI后&#xff0c;为了能让聊天文本可以复制&#xff0c;对消息组件的样式进行修改&#xff0c;主要是移除下面的user-select属性限制&#xff1a; user-select: none;-webkit-user-select: none;-khtml-user-select: none;-moz-user-select: none;-ms…

查看 GitHub 仓库的创建时间

查看 GitHub 仓库的创建时间 1. https://api.github.com/repos/{owner}/{repository}2. curl -s https://api.github.com/repos/{owner}/{repository} | jq .created_atReferences 1. https://api.github.com/repos/{owner}/{repository} REST API endpoints for repositories…

VScode怎么重启

原文链接&#xff1a;【vscode】vscode重新启动 键盘按下 Ctrl Shift p 打开命令行&#xff0c;如下图&#xff1a; 输入Reload Window&#xff0c;如下图&#xff1a;

【无线传感网】WSN数据管理技术

文章目录 WSN数据管理的基本概念以数据为中心的WSN数据库与分布式数据库相比具有的特殊性WSN数据管理技术的研究热点 WSN数据管理的关键技术无线传感器网络数据存储结构网外集中式存储方案网内分层存储方案网内本地存储方案以数据为中心的网内存储方案 数据查询处理技术查询类型…

python调用gemini2.0接口识别图片文字

import os import base64 import google.generativeai as genai# 配置 Google API Key # 可以在系统环境变量设置 GOOGLE_API_KEY GOOGLE_API_KEY os.getenv("GOOGLE_API_KEY", "AIzaSXXXXXXXXXXXXXX") # 替换成你的 API Key# 设置 Gemini 模型名称 mode…

Linux Debian安装ClamAV和命令行扫描病毒方法,以及用Linux Shell编写了一个批量扫描病毒的脚本

ClamAV是一个开源的跨平台病毒扫描引擎&#xff0c;用于检测恶意软件、病毒、木马等安全威胁。 一、Linux Debian安装ClamAV 在Linux Debian系统上安装ClamAV&#xff0c;你可以按照以下步骤进行&#xff1a; 更新软件包列表&#xff1a; 打开终端并更新你的软件包列表&#…

【机器学习篇】穿越数字迷雾:机器深度学习的智慧领航

引言&#xff1a; 在当今科技飞速发展的时代&#xff0c;机器深度学习已成为推动众多领域变革的核心力量&#xff0c;从语音识别到图像分类&#xff0c;从自然语言处理到自动驾驶&#xff0c;其影响力无处不在。深度学习模拟人类大脑的神经网络结构&#xff0c;使计算机能够自…

CAN总线波形中最后一位电平偏高或ACK电平偏高问题分析

参考&#xff1a;https://zhuanlan.zhihu.com/p/689336144 有时候看到CAN总线H和L的差值波形的最后一位电平会变高很多&#xff0c;这是什么原因呢&#xff1f; 实际上这是正常的现象&#xff0c;最后一位是ACK位。问题描述为&#xff1a;CAN总线ACK电平偏高。 下面分析下原因…

B2B营销的新篇章:开源AI智能名片S2B2C商城小程序的应用探索

摘要&#xff1a; B2B营销&#xff0c;作为企业间营销活动的总称&#xff0c;因其独特的业务特性而呈现出不同于B2C营销的显著特征。在数字化转型的大潮中&#xff0c;B2B企业正积极探索新的营销手段以提高效率和竞争力。本文旨在探讨B2B营销的基本特性&#xff0c;并重点引入…

Kotlin在医疗大健康域的应用实例探究与编程剖析(上)

一、引言 1.1 研究背景与意义 在当今数字化时代,医疗行业正经历着深刻的变革。随着信息技术的飞速发展,尤其是人工智能、大数据、物联网等新兴技术的广泛应用,医疗行业数字化转型已成为必然趋势。这种转型旨在提升医疗服务的效率和质量,优化医疗资源配置,为患者提供更加…

【电机控制】基于STC8H1K28的六步换向——方波驱动(软件篇)

【电机控制】基于STC8H1K28的六步换向——方波驱动&#xff08;软件篇&#xff09; 文章目录 [TOC](文章目录) 前言一、main.c二、GPIO.c三、PWMA.c四、ADC.c五、CMP.c六、Timer.c七、PMSM.c八、参考资料总结 前言 【电机控制】STC8H无感方波驱动—反电动势过零检测六步换向法 …

一个在ios当中采用ObjectC和opencv来显示图片的实例

前言 在ios中采用ObjectC编程利用opencv来显示一张图片&#xff0c;并简单绘图。听上去似乎不难&#xff0c;但是实际操作下来&#xff0c;却不是非常的容易的。本文较为详细的描述了这个过程&#xff0c;供后续参考。 一、创建ios工程 1.1、选择ios工程类型 1.2、选择接口模…

arcgis模版空库怎么用(一)

这里以某个项目的数据为例&#xff1a; 可以看到&#xff0c;属性表中全部只有列标题&#xff0c;无数据内容 可能有些人会认为空库是用来往里面加入信息的&#xff0c;其实不是&#xff0c;正确的用法如下&#xff1a; 一、下图是我演示用的数据&#xff0c;我们可以看到其中…

【论文笔记之 Mega-TTS2】Boosting Prompting Mechanisms For Zero-Shot Speech Synthesis

本文对 Ziyue Jiang 等人于 2024 年发表的论文进行简单地翻译。如有表述不当之处欢迎批评指正。欢迎任何形式的转载&#xff0c;但请务必注明出处。 论文链接&#xff1a;https://arxiv.org/pdf/2307.07218 目录 Abstract1. 介绍2. 背景3. 方法3.1. 解耦出韵律和音色3.2. 压缩…

【服务器】上传文件到服务器并训练深度学习模型下载服务器文件到本地

前言&#xff1a;本文教程为&#xff0c;上传文件到服务器并训练深度学习模型&#xff0c;与下载服务器文件到本地。演示指令输入&#xff0c;完整的上传文件到服务器&#xff0c;并训练模型过程&#xff1b;并演示完整的下载服务器文件到本地的过程。 本文使用的服务器为云服…

什么是TDD测试驱动开发(Test Driven Development)?

什么是测试驱动开发&#xff1f; 软件开发团队通常会编写自动化测试套件来防止回归。这些测试通常是在编写应用程序功能代码之后编写的。我们将采用另一种方法&#xff1a;在实现应用程序代码之前编写测试。这称为测试驱动开发 (TDD)。 为什么要应用 TDD&#xff1f;通过在实…

LLaMA详解

LLaMA 进化史 大规模语言模型(Large Language Model, LLM)的快速发展正在以前所未有的速度推动人工智能(AI)技术的进步。 作为这一领域的先行者, Meta在其LLaMA(Large Language Model Meta AI)系列模型上取得了一系列重大突破。 近日, Meta官方正式宣布推出LLaMA-3, 作为继LL…

connect to host github.com port 22: Connection timed out 的解决方法

原因是 Github 被 GFW 屏蔽了。 Windows 系统&#xff0c;打开 C:\Windows\System32\drivers\etc&#xff0c;复制其中的 hosts 文件至桌面&#xff0c;用文本编辑器或者其他工具打开。 复制以下内容进去&#xff1a; 140.82.114.4 github.com 151.101.1.6 github.global.ss…