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抛出指定的异常类型,不会滚事务,也可以指定多个异常类型

注意:

  • 区别 只读事务 和 无事务
  • 只读事务 可以设置隔离级别,默认为可重复读,可设置 isolation 更改隔离级别
  • 无事务 仅为默认的隔离级别(可重复读)

@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.mzph.cn/news/205197.shtml

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

相关文章

如何解决5G基站高能耗问题?

安科瑞 须静燕 截至2023年10月&#xff0c;我国5G基站总数达321.5万个&#xff0c;占全国通信基站总数的28.1%。然而&#xff0c;随着5G基站数量的快速增长&#xff0c;基站的能耗问题也逐渐日益凸显&#xff0c;基站的用电给运营商带来了巨大的电费开支压力&#xff0c;降低5…

vue项目配置多个代理

在本地.env文件配置本地/测试/预发/正式 路径&#xff1a; 在vue.config.js 里面配置&#xff1a; module.exports defineConfig({transpileDependencies: false,lintOnSave: false,outputDir: process.env.VUE_APP_DIST,publicPath: /,css: {loaderOptions: {postcss: {// p…

Motion Plan之轨迹生成代码实现 (1)

Motion Plan之搜索算法笔记Motion Plan之基于采样的路径规划算法笔记Motion Plan之带动力学约束路径搜索 Motion Plan之轨迹生成笔记文章开始前先回顾下上次的带约束的轨迹生成&#xff0c;轨迹生成本质就是曲线拟合。曲线拟合常用的方法有&#xff1a;多项式、贝赛尔曲线、三…

CPU密集型和IO密集型与CPU内核之间的关系

CPU密集型和IO密集型与CPU内核之间的关系 一、CPU密集型 介绍 CPU密集型&#xff0c;也叫计算密集型&#xff0c;是指需要大量CPU计算资源&#xff0c;例如大量的数学运算、图像处理、加密解密等。这种类型的任务主要依赖于CPU的计算能力&#xff0c;会占用大量的CPU时间片&am…

vcomp140.dll文件丢失解决方法分享:三种亲测有效方案

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“vcomp140.dll丢失”。这个错误提示通常出现在运行某些程序或游戏时&#xff0c;给使用者带来了很大的困扰。那么&#xff0c;vcomp140.dll丢失怎样修复呢&#xff1f;本文将详细介绍解决这…

网工内推 | 国企网工、运维,厂商认证优先,13薪,带薪年假

01 中百集团 招聘岗位&#xff1a;运维工程师 职责描述&#xff1a; 1、对集团内使用云计算架构&#xff08;Kubernetes&#xff09;的系统进行规划、运维及管理相关工作。 2、对集团数据中心系统的大数据基础架构&#xff08;Cloudera Distribution Hadoop&#xff09;的规划…

打补丁,生成.diff文件

作者&#xff1a;爱塔居 文章目录 目录 前言 步骤 一、在根目录上&#xff0c;输入添加指令 二、输入修改内容指令 三、生成补丁 前言 自己的理解&#xff0c;仅供参考&#xff0c;欢迎指正。 补丁的话&#xff0c;在我看来就是方便评审&#xff0c;更方便看修改代码吧。 步骤…

Python-关系运算符详解

关系运算符&#xff1a;比较两个操作数的大小或者相等关系 < > ! 1、关系运算符的关系表达式返回值是布尔类型bool 成立就是真&#xff0c;即1&#xff1b;不成立就是假&#xff0c;即0 2、关系运算符还可以比较字符 字符根据字典序比较&#xff0c;先看首字母在…

智能优化算法应用:基于人工大猩猩部队算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于人工大猩猩部队算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于人工大猩猩部队算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.人工大猩猩部队算法4.实验参数设…

Unity Meta Quest 一体机开发(九):【手势追踪】通过录制抓取手势实现自定义抓取姿势

文章目录 &#x1f4d5;教程说明&#x1f4d5;录制前的准备&#x1f4d5;第一种录制方法&#xff08;Hand Grab Pose Tool 场景&#xff09;⭐在运行模式中确认录制⭐保存录制的手势&#xff0c;将物体做成 Prefab⭐在编辑阶段调整抓取手势&#x1f50d;Fingers Freedom&#x…

德国进口高速主轴电机在机器人上的应用及选型方案

随着机器人技术的日新月异&#xff0c;高速主轴电机在机器人领域的应用也日趋广泛。德国进口的SycoTec高速主轴电机&#xff0c;以其高转速、高精度、高刚度的特点&#xff0c;在机器人的切割、铣削、钻孔、去毛刺等加工应用中发挥着关键作用。 一、高速主轴电机的特点 SycoT…

微信机器人接口开发

简要描述&#xff1a; 设置http回调地址 请求URL&#xff1a; http://域名地址/setHttpCallbackUrl POST 请求头Headers:&#xff08;别忘了传&#xff09; Content-Type&#xff1a;application/jsonAuthorization&#xff1a;login接口返回 参数&#xff1a; 参数名必…

了解Linux网络配置

本章主要介绍网络配置的方法。 网络基础知识 查看网络信息 图形化界面修改 通过配置文件修改 命令行管理 11.1 网络基础知识 一台主机需要配置必要的网络信息&#xff0c;才可以连接到互联网。需要的配置网络信息包括IP、 子网掩码、网关和 DNS。 11.1.1 IP 地址 在计算机…

1.10 C语言之外部变量与作用域

1.10 C语言之外部变量与作用域 一、外部变量概述二、练习 一、外部变量概述 我们说&#xff0c;函数&#xff08;不管是main函数还是其他函数&#xff09;内部定义的变量&#xff0c;其作用范围都只在函数内部&#xff0c;我们把这些变量叫做自动变量或者局部变量。除了局部变…

嵌入式工程师校招经验与学习路线总结

前言&#xff1a;不知不觉2023年秋招已经结束&#xff0c;作者本人侥幸于秋招中斩获数十份大差不差的OFFER&#xff0c;包含&#xff1a;Top级的AIGC&#xff0c;工控龙头&#xff0c;国产MCU原厂&#xff0c;医疗器械&#xff0c;新能源车企等。总而言之&#xff0c;秋招总体情…

HarmonyOS开发工具DevEco Studio的下载和安装

一、DevEco Studio概述 一、下载安装鸿蒙应用开发工具DevEco Studio 开发鸿蒙应用可以从鸿蒙系统上运行第一个程序Hello World开始。 为了得到这个Hello World&#xff0c;你需要得到这个Hello World的源代码&#xff0c;源代码是用人比较容易看得懂的计算机编程语言规范写的…

leetcode做题笔记1466. 重新规划路线

n 座城市&#xff0c;从 0 到 n-1 编号&#xff0c;其间共有 n-1 条路线。因此&#xff0c;要想在两座不同城市之间旅行只有唯一一条路线可供选择&#xff08;路线网形成一颗树&#xff09;。去年&#xff0c;交通运输部决定重新规划路线&#xff0c;以改变交通拥堵的状况。 路…

排序:直接选择排序

直接选择排序&#xff1a; 本质&#xff1a; 直接选择排序的本质就是在数组中进行遍历挑选出最大的元素&#xff0c;讲最大的元素放到对应的位置后&#xff0c;再次选出次大的位置&#xff0c;而后又放到对应的位置..........................直到数组成为一个有序序列。 优…

Leetcode刷题笔记题解(C++):LCR 121. 寻找目标值 - 二维数组

思路&#xff1a;从左小角或者右上角开始遍历&#xff0c;假设右上角开始遍历&#xff0c;如果当前值大于目标值则列-1&#xff1b;如果当前值小于目标值则行1&#xff0c;以此遍历来查找目标值&#xff1b;注意col和row的选取 class Solution { public:bool findTargetIn2DPl…

异常检测 | 基于孤立森林(Isolation Forest)的数据异常数据检测(结合t-SNE降维可视化)

异常检测 | MATLAB实现基于孤立森林的数据异常检测 目录 异常检测 | MATLAB实现基于孤立森林的数据异常检测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 Matlab实现基于孤立森林(Isolation Forest)的数据异常数据检测可视化&#xff08;完整源码和数据) 基于孤立森林(…