2023.12.6 关于 Spring Boot 事务的基本概念

目录

事务基本概念

前置准备

Spring Boot 事务使用 

编程式事务

声明式事务

@Transactional 注解参数说明

@Transational 对异常的处理

解决方案一

解决方案二

@Transactional 的工作原理

面试题 

Spring Boot 事务失效的场景有那些?


事务基本概念

  • 事务指一组操作,这些操作要么全部成功,要么全部失败
  • 如果在一组操作中有一个操作失败了,那么整个事务便会回滚即撤销已经执行的操作,从而保证数据的一致性和完整性

实例理解

  • 典型实际场景为 银行转账操作
两个步骤
  1. 从源账户扣除指定金额
  2. 将该金额添加到目标账户
分析原因
  • 这两个步骤必须保证同时执行成功,如果其中任意一个步骤失败,便必须撤销整个操作,以保持数据的一致性
  • 即 在扣款成功后,如果存款时发生错误(如网络问题),那么我们必须要回滚扣款操作,以确保不会错误地从源账户中扣款

前置准备

  •  下述实例均基于 实现根据用户 id 删除用户信息功能

  • 创建一个 user 表,并添加几条用户信息

  • 创建 User 实体类 与 数据库的 user 表字段名相对应
import lombok.Data;@Data
public class User {private int id;private String name;private int age;private String password;private int state;
}
  • 初始化 UserMapper 接口,此处我们添加一个 del 方法
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;//添加 @Mapper 注解 代表该接口会伴随这 项目的启动而注入到容器中
@Mapper
public interface UserMapper {//    根据用户id 删除用户信息int del(@Param("user_id") int id);
}
  • 在与 接口相对应的 XML 文件中添加上与 del 方法 相对应的 sql 语句
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper"><delete id="del">delete from user where id = #{user_id}</delete>
</mapper>

Spring Boot 事务使用 

编程式事务

  • Spring Boot 中内置了两个对象
  • DataSourceTransactionManager 用来获取事务(开启事务)、提交或回滚事务
  • TransactionDefinition 为事务的属性,在获取事务的时候需要将其 传递进去,从而获得一个事务 TransactionStatus
实例理解
  • 我们在 UserController 中 使用编程式事务给 根据用户id 删除用户信息  这一功能加上事务
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;@ResponseBody
@Controller
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;//    编程式 事务@Autowiredprivate DataSourceTransactionManager transactionManager;@Autowiredprivate TransactionDefinition transactionDefinition;@RequestMapping("/del")public String del(Integer id) {if(id < 0 || id == null)  return "请输入正确的用户 id";TransactionStatus transactionStatus = null;int result = 0;try {
//            1. 开启事务transactionStatus = transactionManager.getTransaction(transactionDefinition);
//            2. 业务操作 删除用户result = userService.del(id);System.out.println("del 方法:" + (result == 1 ? "删除成功": "删除失败" ));
//            3. 提交事务transactionManager.commit(transactionStatus); // 提交事务}catch (Exception e) {if(transactionStatus != null){
//              发生异常 回滚事务transactionManager.rollback(transactionStatus); // 回滚事务}}return "del 方法:" + (result == 1 ? "删除成功": "删除失败" );}
}

测试结果

  • 在浏览器的 URL 地址框中输入相对应地址,来调用上述代码的方法


声明式事务

  • Spring Boot 提供了 @Transactional 注解实现事务
  • 只需在需要的方法上添加 @Transaction 注解即可
  • 无需手动开启事务和提交事务,进入方法时自动开启事务,方法执行完会自动提交事务
  • 如果中途发生了没有处理的异常会自动回滚事务

注意:

  • @Transactional 注解可以用来修饰方法或类
  • 修饰方式时,该方法必须为 public 否则不生效
  • 修饰类时,表明该注解对该类中所以的 public 方法都生效

实例理解

  • 我们在 UserController2 中 使用声明式事务给 根据用户id 删除用户信息  这一功能加上事务
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/user2")
public class UserController2 {@Autowiredprivate UserService userService;@RequestMapping("/del")@Transactionalpublic String del(Integer id) {if(id < 0 || id == null) return "请输入正确的用户 id";int result = userService.del(id);return "del 方法:" + (result == 1 ? "删除成功": "删除失败" );} 
}

测试结果

  • 在浏览器的 URL 地址框中输入相对应地址,来调用上述代码的方法


@Transactional 注解参数说明

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

注意:

  • 区别 只读事务 和 无事务
  • 只读事务 可以设置隔离级别,默认为 repeatable read ,可设置 isolation 更改隔离级别

  • 无事务 仅为默认的隔离级别 repeatable read

@Transational 对异常的处理

实例理解
  • 此处我们故意在 UserController 中加入异常代码,并手动捕获该 算数异常
  • 那么此处 @Transational 是否会回滚 del 操作呢?
package com.example.demo.controller;import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/user2")
public class UserController2 {@Autowiredprivate UserService userService;@RequestMapping("/del")@Transactional // 在方法执行前开启事务 方法正常执行完后提交事务 执行途中发生异常回滚事务public String del(Integer id) {if(id < 0 || id == null) return "请输入正确的用户 id";int result = userService.del(id);try {int num = 10/0;} catch (Exception e) {e.printStackTrace();}return "del 方法:" + (result == 1 ? "删除成功": "删除失败" );}
}

执行结果:

  • 在浏览器的 URL 地址框中输入相对应地址,来调用上述代码的方法

  • 由上图可知此处我们的 @Transational 并未回滚 del 操作
  • 即 @Transactional 在异常被捕获的情况下,不会进行事务的自动回滚
解决方案一
  • 捕获到异常后,再重新抛出异常,让框架感知到异常,如果框架感知到异常,便会自动回滚事务
@RequestMapping("/del")
@Transactional // 在方法执行前开启事务 方法正常执行完后提交事务 执行途中发生异常回滚事务public String del(Integer id) {if(id < 0 || id == null) return "请输入正确的用户 id";int result = userService.del(id);try {int num = 10/0;} catch (Exception e) {e.printStackTrace();
//        抛出异常throw e;}return "del 方法:" + (result == 1 ? "删除成功": "删除失败" );
}

执行结果:

  • 在浏览器的 URL 地址框中输入相对应地址,来调用上述代码的方法

  • 数据库中 id = 1 的 xiaolin 未被删除,说明此时 @Transational 进行了回滚操作
解决方案二
  • 捕获到异常后,手动回滚事务,此处框架是感知不到异常的
@RequestMapping("/del")
@Transactional // 在方法执行前开启事务 方法正常执行完后提交事务 执行途中发生异常回滚事务public String del(Integer id) {if(id < 0 || id == null) return "请输入正确的用户 id";int result = userService.del(id);try {int num = 10/0;} catch (Exception e) {e.printStackTrace();
//        手动回滚事务TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return "del 方法:" + (result == 1 ? "删除成功": "删除失败" );
}
  • TransactionAspectSupport 为 Spring 框架中的一个类,提供了事务相关的支持和功能
  • currentTransactionStatus 为 TransactionAspectSupport 类的一个静态方法,用于获取当前事务的状态对象
  • setRollbackOnly 为 TransactionStatus 接口的一个方法,用于将当前事务标记为回滚状态0

执行结果:

  • 在浏览器的 URL 地址框中输入相对应地址,来调用上述代码的方法

  • 数据库中 id = 1 的 xiaolin 未被删除,说明此时 @Transational 进行了回滚操作

重点理解

  • 此处为什么会返回一个 删除成功?
  • 代码从上到下顺序执行,先执行了 del 操作
  • 即此处的 result 值已经成功被赋值为 1 (返回值为 del 操作影响的行数)
  • 然后我们才对 算数异常进行捕获,捕获之后再进行的 回滚操作
  • 且异常捕获之后,我们并未抛出异常,从而不会出现方案一的服务器错误
  • 在捕获完异常后代码将继续向下执行,此时便返回 "del 方法:" + (result == 1 ? "删除成功": "删除失败" )
  • 因为此处的 result 等于 1,所以返回了一个 删除成功
  • 但是我们要明白的是 我们在捕获异常后,在处理异常时进行了事务的回滚
  • 所以此处数据库中的  id = 1 的 xiaolin 未被删除

@Transactional 的工作原理

  • 此处声明式事务的实现方式 可使用 Spring AOP 来实现
  • 执行目标方法之前 先开启事务,类似于前置通知
  • 执行完目标方法之后 再提交事务,类似于后置通知
  • 如果在执行中途发生了没有处理的异常 便回滚事务
  • 综上 我们可以直接将目标方法 写入环绕通知中 
/*
* 环绕通知
* 此处的 joinPoint 就是连接点,即方法本身
* */
@Around("pointcut()")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {Object obj = null;System.out.println("执行目标方法之前 这里开启事务");try {
//        此处执行目标方法obj = joinPoint.proceed();}catch (Exception e) {System.out.println("执行目标方法出现异常 这里回滚事务");}System.out.println("执行目标方法之后 这里提交事务");
//    最后将执行的结果交给框架return obj;
}
  • 上述代码仅为 实现声明式事务 的大致思路

面试题 

Spring Boot 事务失效的场景有那些?

  1. @Transactional 修饰的方法为非 public ,导致事务失效
  2. @Transactional 设置了一个较小的超时时间,如果方法本身的执行时间超过了设置的 timeout 超时时间,那么就会导致本来应该正常插入数据的方法执行失败
  3. 代码中有 try/catch 语句,仅捕获异常,不进行额外处理,则导致 @Transactional  不会自动回滚事务
  4. 数据库不支持事务,我们程序中的 @Transactional 只是给调用的数据库发生了:开始事务、提交事务 或 回滚事务 的之类,但是如果数据库本身不支持事务,如 MySQL 中设置了使用 MySAM 引擎,那么它本身是不支持事务的,在这种情况下,即使在程序中添加了 @Transactional 注解,那么依然不会有事务行为
  5. 当调用类内部的 @Transactional 修饰的方法时,事务是不会生效的
@RequestMapping("/save")
public int saveMappping(UserInfo userInfo) {return save(userInfo);
}
@Transactional
public int save(UserInfo userInfo) {// 非空效验if (userInfo == null ||!StringUtils.hasLength(userInfo.getUsername()) ||!StringUtils.hasLength(userInfo.getPassword()))return 0;int result = userService.save(userInfo);int num = 10 / 0; // 此处设置一个异常return result;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【AI神器】CodeGeeX智能编程助手

目录 1.CodeGeeX介绍2.IDEA插件安装3.助手功能3.1 代码分析&#xff08;/explain&#xff09;3.2 代码自动补全3.3 AI优化代码3.4 代码添加注释&#xff08;/comment&#xff09;3.5 AI添加文档3.6 单元测试生成&#xff08;/tests&#xff09;3.7 bug查找修正&#xff08;/fix…

[OpenWrt]RAX3000一根线实现上网和看IPTV

背景&#xff1a; 1.我家电信宽带IPTV 2.入户光猫&#xff0c;桥接模式 3.光猫划分vlan&#xff0c;将上网信号IPTV信号&#xff0c;通过lan口&#xff08;问客服要光猫超级管理员密码&#xff0c;具体教程需要自行查阅&#xff0c;关键是要设置iptv在客户侧的vlan id&#…

Maven基础

目录 Maven坐标 坐标简介 主要组成 Maven依赖管理 配置依赖 依赖简介 配置依赖 依赖传递 依赖传递简介 排除依赖 依赖范围 生命周期 生命周期简介 执行指定生命周期 Maven坐标 坐标简介 Maven中的坐标是资源的唯一标识&#xff0c;通过该坐标可以唯一定位资…

Yolov5双目测距-双目相机计数及测距教程(附代码)

引言 在计算机视觉领域&#xff0c;Yolov5-Binocular相机距离计数及测距是一个引人注目的研究方向。本教程将为小白用户提供一个简明扼要的学习指南&#xff0c;涵盖了关键步骤&#xff0c;包括标定、公示推倒以及重要的代码片段。 第一步&#xff1a;环境搭建 首先&#x…

cpp:1:10: fatal error: opencv2/core.hpp: 没有那个文件或目录

前言&#xff1a; 我按照官网方法安装了opencv&#xff0c;运行的也是官网的测试代码&#xff1a; #include <opencv2/core.hpp> #include <opencv2/highgui.hpp> using namespace cv; int main() {printf("hello world")return 0; } 半解决&#xff…

07用户行为日志数据采集

用户行为数据由Flume从Kafka直接同步到HDFS&#xff0c;由于离线数仓采用Hive的分区表按天统计&#xff0c;所以目标路径要包含一层日期。具体数据流向如下图所示。 按照规划&#xff0c;该Flume需将Kafka中topic_log的数据发往HDFS。并且对每天产生的用户行为日志进行区分&am…

java-sec-code的xss

java-sec-code 用于学习java漏洞代码 环境部署 直接在idea中使用git 运行即可 RequestMapping("/reflect") ResponseBody public static String reflect(String xss) {return xss;}当用户访问到/reflect URL地址时&#xff0c;程序会自动调用reflect方法&#xff0c…

JS对象笔记

对象声明 对象也只是一种数据类型/字面值。写对象这个字面值有两种写法&#xff0c;一种是普通的对象&#xff0c;这种对象用new 构造函数&#xff08;&#xff09;&#xff0c;另一种是JS内特有的json对象。这个对象是直接{}就代表对象。且也是在堆内。 对象的构成 无论是上…

Blender学习--制作带骨骼动画的机器人

1. 首先创建一个机器人模型 时间关系&#xff0c;这部分步骤有时间补充 2. 然后为机器人创建一副骨架 时间关系&#xff0c;这部分步骤有时间补充 3.骨骼绑定 切换到物体模式&#xff0c;选中机器人头部&#xff0c;Shift选中骨骼&#xff0c;切换到姿态模式&#xff0c;&am…

《Easy3d+Qt+VTK》学习

《Easy3dQtVTK》学习-1、编译与配置 一、编译二、配置注 一、编译 1、 资源下载&#xff1a;easy3d giuhub 2、解压缩 3、用qt打开CMakeLists.txt即可 4、点击项目&#xff0c;选择debug或者release&#xff0c;图中3处可自行选择&#xff0c;因为我的qt版本是6&#xff0c…

【TC3xx】GETH

目录 一、RGMII 二、SMI接口 三、TC3xx MCAL 3.1 MCU 3.2 Port 3.3 DMA 3.4 中断配置 3.5 ETH 3.6 集成 一、RGMII TC3xx支持MII/RMII/RGMII三种以太网数据通信接口。其中RGMII经常用于MAC和MAC之间&#xff0c;或MAC与PHY之间的通信&#xff0c;RGMII的带宽可以是10M…

ViTDet论文笔记

arxiv&#xff1a;https://arxiv.org/abs/2203.16527 GitHub&#xff1a;https://github.com/ViTAE-Transformer/ViTDet 摘要 本文提出使用plain&#xff0c;non-hierarchical视觉transformer作为目标检测的主干网络。通过这种设计可以使得ViT结构模型不需要再重新设计一个分…

51单片机控制1602LCD显示屏输出两行文字一

51单片机控制1602LCD显示屏输出两行文字一 1.概述 这篇文章介绍1602型号显示屏的基础知识&#xff0c;以及使用单片机控制它输出两行内容。 2.1602基础知识 1602 液晶显示模块是一种通用的工业液晶显示模块&#xff0c;专门用来显示字母、数字、符号等的点阵型液晶显示模块…

Windows 安全基础——NetBIOS篇

Windows 安全基础——NetBIOS篇 1. NetBIOS简介 NetBIOS&#xff08;Network Basic Input/Output System, 网络基本输入输出系统&#xff09;是一种接入服务网络的接口标准。主机系统通过WINS服务、广播及lmhosts文件多种模式&#xff0c;把NetBIOS名解析对应的IP地址&#xf…

IntelliJ IDEA无公网远程连接Windows本地Mysql数据库提高开发效率

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《Linux》《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;…

资产侦查灯塔系统ARL部署

在docker和docker-compose都安装好的前提下进行部署 随便创建一个目录 mkdir docker_arl 切换到该目录 cd docker_arl 下面步骤是安装pip&#xff0c;如果已安装可以直接跳到wget命令下载灯塔系统文件 &#xff08;但是我不确定pip版本是否有影响&#xff0c;你也可以将命…

iOS按钮控件UIButton使用

1.在故事板中添加按钮控件,步聚如下: 同时按钮Shift+Commad+L在出现在控件库中选择Button并拖入View Controller Scene中 将控件与变量btnSelect关联 关联后空心变实心 如何关联?直接到属性窗口拖按钮变量到控件上,出现一条线,然后松开,这样就关联成功了 关联成功后属性窗口…

Clickhouse RoaringBitmap

https://blog.csdn.net/penriver/article/details/119736050 https://juejin.cn/post/7179956435806076988 BitMap适合连续密集的正整数存储&#xff0c;对于稀疏的正整数存储&#xff0c;其性能在很多时候是没办法和int数组相比的&#xff0c;尤其是正整数跨度较大的场景&…

实验制备高纯酸PFA酸纯化器材质分析,SCH亚沸蒸馏器特点是什么

.酸纯化器&#xff1a;也称酸蒸馏器、高纯酸提取系统、酸纯化系统、亚沸腾蒸馏器、高纯酸蒸馏纯化器。常规实验室分析中&#xff0c;各种酸及试剂被广泛应用于日常的样品处理及分析中。那么应该选用什么材质的酸纯化器呢 氟塑料酸纯化器&#xff0c;提纯酸效果好&#xff0c;避…

CentOS7安装 Docker Compose

docker系列 CentOS7安装 Docker Compose docker系列前言1、下载 Docker Compose2、 授权执行权限3、添加软链接4、验证安装 前言 下面的操作是在centos7中完成的。这里安装的是2.23.3版本的docker-compose。 1、下载 Docker Compose 确保你具有 curl 工具&#xff0c;然后使用…