7.8 SpringBoot事务@Transactional实战 管理员借阅审核

news2025/1/15 13:05:50

CSDN成就一亿技术人

文章目录

  • 前言
  • 一、事务是用来干什么的?事务是用来解决什么问题的?
  • 二、事务执行的流程
  • 三、事务的ACID四大特性
  • 四、事务的四种隔离级别
  • 五、声明式事务@Transactional
  • 六、web层实战
  • 七、service层实战
    • 7.1 BookBorrowService
    • 7.2 细数BookBorrowServiceImpl实战细节
      • 7.2.1 rollbackFor
      • 7.2.2 @Transactional不生效的场景
  • 八、dal层实战
  • 九、测试和Git提交
  • 最后


前言

通过上文我们实现了图书借阅审核列表,本文实战的场景是:审核图书借阅!因为审核后最终会执行2条更新SQL,所以就派上了事务
事务是用来干什么的?事务是用来解决什么问题的? 什么情况下用到事务?
对于SpringBoot,我们主要使用的是声明式事务@Transactional,不仅有实战的用法,我还会用白话讲解事务的执行流程ACID四大特性MySQL四种隔离级别,以及 @Transactional不生效场景 坑点 (也适用于其它AOP注解)!

掌握这些应对大部分项目绰绰有余,面对一般面试也会尽在掌握中,OK,话不多说,嗨一波,Let’s Go!


一、事务是用来干什么的?事务是用来解决什么问题的?

  • 事务是用来干什么的?干嘛的?
  • 事务是用来解决什么问题的?
  • 事务是用来保证什么的?
  • 事务一般用在什么地方?
  • 什么情况下用到事务?

如果你有上面这些疑问,我想通过本文的审核场景 来回答你的疑问!

为了方便你理解需求,我画了一个流程图,如下:

在这里插入图片描述

审核通过或驳回后主要干两件事:

  1. 修改 图书借阅表book_borrowing的状态status字段(1-通过,2-驳回)

  2. 审核通过:修改 图书表book的借阅数量borrow_count+1

    驳回:修改 图书表bookstatus字段(0-空闲)

那么这时问题就来了,不管是通过还是驳回,一个方法内都需要更新2条SQL,那么这时如果出现一个sql成功,一个sql失败,数据不就不一致了吗?
那怎么保证2个SQL要么全成功,要么全失败,保证数据的一致性呢?

那么在这种情况下就需要SQL事务了,简单来说:用它来保证多条SQL的数据一致!!!

怎么保证的呢?具体看下面的执行流程~


二、事务执行的流程

对于事务动作行为有3种:

  • 开始 begin
  • 提交 commit
  • 回滚 rollback

那么串起来看一下它的执行流程,如下图:

在这里插入图片描述

从流程可以看出来,不管执行多少条SQL,最终都会一起【提交】或【回滚】!也就能保证多条SQL要么全成功,要么全失败,保证数据的一致!!!


三、事务的ACID四大特性

谈到事务,不得不说事务的四大特性,尽管是老生常谈,也是面试常见问题,但依然有很多人说不明白,因为确实太抽象,这里我想用白话的方式助你打通你的任督二脉:

  • 原子性(Atomicity):就像化学中学过的原子,最小单位不可拆分,是一个整体,要么全成功,要么全失败;
  • 一致性(Consistency):事务不管怎么搞,最终的数据都是一致的;
  • 隔离性(Isolation):不同事务之间是隔离的,具体隔离的度取决于对应数据库的事务隔离级别;
  • 持久性(Durability):事务结束以后数据会持久化

四、事务的四种隔离级别

上面提到的数据库的事务隔离级别,每个数据库的实现不尽相同,例如MySQL实现了4种隔离级别:

  • 读未提交(Read uncommitted):A事务能读到B事务执行过但未提交的SQL的结果
  • 读已提交(Read committed):A事务能读到B事务执行过也已提交的SQL的结果
  • 可重复读(Repeatable read):A事务对于同一个查询SQL,每次读取的结果也是相同的,尽管B事务已经修改了结果并提交了事务
  • 串行化(Serializable):A事务与B事务按顺序执行

因为【读未提交】会产生脏读,【串行化】性能低,所以常用的是【读已提交】和【可重复读】,因为【可重复读】在MySQL会有【间隙锁】,所以更多会使用【读已提交】这个事务隔离级别。

如果你想了解更多,像脏读、幻读、间隙锁、MVCC机制,推荐阅读我之前写的MySQL相关文章,例如:

【MySQL】事务隔离机制 – 必须说透

【MySQL】MVCC原理分析 + 源码解读 – 必须说透


五、声明式事务@Transactional

在Spring中,事务的实现有声明式事务编程式事务

  • 声明式事务
    就是利用AOP切面实现,就像 【7.6 SpringBoot AOP实战 统一角色权限校验】,定义注解实现切面。

    声明式事务正是通过@Transactional注解 控制事务的提交和回滚!对代码无侵入性!

  • 编程式事务
    就是编程直接手写,手动控制事务的提交和回滚!对代码有侵入性,好处是灵活,可以做到代码块级别!

在SpringBoot中,当然推荐使用【声明式事务】AOP的实现@Transactional注解。具体使用实战代码继续向下看~


六、web层实战

在这里插入图片描述

对于借阅审核API,依然定义在BookAdminController中,post请求(@PostMapping) + body传参(@RequestBody),并支持管理员角色校验@Role,最后返回通用泛型结果TgResult<String> ,代码如下:

@Role
@PostMapping("/book/borrow/examine")
public TgResult<String> examineBookBorrow(@RequestBody ExamineBookBorrowParamVO paramVO) {
    return bookBorrowService.examineBookBorrow(paramVO.getBorrowId()
            , paramVO.getApproved(), paramVO.getRejectReason());
}

其中的VO入参对象定义如下:

@Data
public class ExamineBookBorrowParamVO implements Serializable {
    private Integer borrowId;
    // 是否审核通过
    private Boolean approved;
    // 驳回原因
    private String rejectReason;
}

七、service层实战

在这里插入图片描述

7.1 BookBorrowService

在BookBorrowService中,新增 审核图书借阅 examineBookBorrow 方法定义

public interface BookBorrowService {
    /**
     * 审核图书借阅
     **/
    TgResult<String> examineBookBorrow(Integer borrowId, Boolean approved, String rejectReason);
}

7.2 细数BookBorrowServiceImpl实战细节

在这里插入图片描述

写的比较细节,实战就更不能忽略细节!分3块解释如下:

    1. 校验
    1. 更新 图书借阅表
    1. 更新 图书表

每块的代码,我相信你都能看的懂,如果跟着学到这还看不懂的,需要补补前面的知识啦~~~

@Autowired
private BookMapperExt bookMapperExt;

@Transactional(rollbackFor = Exception.class)
@Override
public TgResult<String> examineBookBorrow(Integer borrowId, Boolean approved, String rejectReason) {
    // 1. 校验
    BookBorrowing existsPo = bookBorrowingMapper.selectByPrimaryKey(borrowId);
    if (existsPo == null) {
        // 不存在
        return TgResult.fail("400", "图书借阅记录不存在! id=" + borrowId);
    }
    if (!BookBorrowStatusEnum.TO_BE_EXAMINE.getCode().equals(existsPo.getStatus())) {
        // 借阅状态不是:待审核
        return TgResult.fail("400", "图书借阅状态不是待审核状态!");
    }

    // 2.更新 图书借阅表
    BookBorrowing po = new BookBorrowing();
    po.setId(borrowId);
    po.setStatus(BookBorrowStatusEnum.getStatusCode(approved));
    po.setRejectReason(rejectReason);
    po.setVerifyTime(new Date());
    po.setVerifyUserId(AuthContextInfo.getAuthInfo().loginUserId());
    int rows = bookBorrowingMapper.updateByPrimaryKeySelective(po);

    if (rows > 0) {
        // 3.更新图书表
        Integer bookId = existsPo.getBookId();
        if (Boolean.TRUE.equals(approved)) {
            // 审核通过, 图书借阅次数+1。
            // 这里之所以不用updateByPrimaryKeySelective, 是因为如果不查一次mysql不知道现在的borrowCount
            bookMapperExt.increaseBorrowCount(bookId);
        } else {
            // 审核驳回, 图书状态修改为:空闲
            Book book = new Book();
            book.setId(bookId);
            book.setStatus(BookStatusEnum.FREE.getCode());
            bookMapper.updateByPrimaryKeySelective(book);
        }
    }
    return TgResult.ok();
}

7.2.1 rollbackFor

除了方法内的代码,主要说一下为什么是@Transactional(rollbackFor = Exception.class)?而不是直接加的@Transactional

这里的rollbackFor属性,从命名上看肯定是与回滚有关,没错,是指遇到什么异常时才回滚!
默认是在抛出运行时异常(RuntimeException及其子类)时才回滚该事务,但是非RuntimeException的异常抛出时,是不会回滚事务的,所以我们指定Exception就可以将异常一网打尽!

指定Exception.class,凡是抛出异常就回滚!

7.2.2 @Transactional不生效的场景

最后说一下@Transactional不生效的场景,也就是AOP切面注解不起作用的场景,也是我们开发时注意的点,常见的坑点如下:

  • @Transactional注解加在了private方法上
  • @Transactional注解加在了final方法上
  • @Transactional注解虽然加在了public方法上,但是被同一个类中的方法直接内部调用(而调用方法未加@Transactional)
  • 所在方法内 try catch 吃掉了异常,或者抛的异常rollbackFor没有覆盖,造成没有回滚
  • 所在类本身未被spring管理
  • 未开启事务等等。。。

其实造成@Transactional 不生效,都与AOP的实现原理相关,也适用于其它AOP注解失效!
这里很重要,但由于篇幅原因,并未展开深入讲解,看大家的需要我再安排吧,如果还想要讲的更深入,请投票或评论反馈!


八、dal层实战

新建BookMapperExt接口,实现审核通过后,该图书的借阅数量+1

package org.tg.book.dal.mapper.ext;

public interface BookMapperExt {
    int increaseBorrowCount(Integer id);
}

自动生成BookMapperExt.xml,并增加<update>标签

<?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="org.tg.book.dal.mapper.ext.BookMapperExt">

    <update id="increaseBorrowCount">
        update book set borrow_count=borrow_count+1
        where id=#{id}
    </update>

</mapper>

如果Mybatis Update标签生疏了,复习入口:5.5 Mybatis Update标签实战


九、测试和Git提交

留给大家测试:

  • 在SQL1更新成功后,方法内抛出异常,查看SQL1是否更新到了数据库!
  • 在SQL1和SQL2更新成功后,方法内抛出异常,查看SQL1和SQL2是否更新到了数据库!

养成好习惯,每个小功能,一步一提交!
在这里插入图片描述


最后

想要看更多实战好文章,还是给大家推荐我的实战专栏–>《基于SpringBoot+SpringCloud+Vue前后端分离项目实战》,由我和 前端狗哥 合力打造的一款专栏,可以让你从0到1快速拥有企业级规范的项目实战经验!

具体的优势、规划、技术选型都可以在《开篇》试读!

订阅专栏后可以添加我的微信,我会为每一位用户进行针对性指导!

另外,别忘了关注我:天罡gg ,发布新文不容易错过: https://blog.csdn.net/scm_2008

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

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

相关文章

【Linux命令详解 | chmod命令】 chmod命令用于修改文件或目录的权限,保护文件安全性。

文章目录 简介一&#xff0c;参数列表二&#xff0c;使用介绍1. 修改用户权限2. 修改用户组权限3. 修改其他用户权限4. 同时修改多个权限5. 使用数字模式设置权限6. 递归修改目录权限 总结 简介 在Ubuntu系统中&#xff0c;chmod命令是一个强大的工具&#xff0c;用于修改文件…

数据库技术--数据库引擎,数据访问接口及其关系详解(附加形象的比喻)

目录 背景数据库引擎Jet数据库&#xff1a;ISAM&#xff1a;ODBC&#xff08;Open Database Connectivity&#xff09;&#xff1a; 数据访问接口ADO&#xff08;ActiveX Data Objects&#xff09;DAO&#xff08;Data Access Objects&#xff09;RDO&#xff08;Remote Data O…

美团视觉GPU推理服务部署架构优化实战

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

由于找不到vcruntime140.dll,无法继续执行代码,有什么修复方法比较推荐

首先我们在解决vcruntime140.dll问题前&#xff0c;先了解一下vcruntime140.dll是什么&#xff0c;它有什么用途跟作用。 vcruntime140.dll是Microsoft 安装程序的一部分&#xff0c;它是Windows操作系统中的一个动态链接库文件。该文件包含了一些常用的运行时函数和变量&…

Leetcode-每日一题【剑指 Offer 24. 反转链表】

题目 定义一个函数&#xff0c;输入一个链表的头节点&#xff0c;反转该链表并输出反转后链表的头节点。 示例: 输入: 1->2->3->4->5->NULL输出: 5->4->3->2->1->NULL 限制&#xff1a; 0 < 节点个数 < 5000 解题思路 1.题目要求我们反转…

[NOIP2003 普及组] 栈

题目背景 栈是计算机中经典的数据结构&#xff0c;简单的说&#xff0c;栈就是限制在一端进行插入删除操作的线性表。 栈有两种最重要的操作&#xff0c;即 pop&#xff08;从栈顶弹出一个元素&#xff09;和 push&#xff08;将一个元素进栈&#xff09;。 栈的重要性不言自…

【佳佳怪文献分享】MVFusion: 利用语义对齐的多视角 3D 物体检测雷达和相机融合

标题&#xff1a;MVFusion: Multi-View 3D Object Detection with Semantic-aligned Radar and Camera Fusion 作者&#xff1a;Zizhang Wu , Guilian Chen , Yuanzhu Gan , Lei Wang , Jian Pu 来源&#xff1a;2023 IEEE International Conference on Robotics and Automat…

c语言——斐波那契数列应用

//斐波那契数列应用 #include<stdio.h> int main() {int i,n,t10,t21,nextTerm;printf("输出项目数&#xff1a;");scanf("%d",&n);printf("斐波那契数列应用&#xff1a;");for(i1;i<n;i){printf("%d、",t1);nextTermt1…

「C/C++」C/C++搭建程序框架

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「UG/NX」NX二次开发「UG/NX」BlockUI集合「VS」Visual Studio「QT」QT5程序设计「C/C」C/C程序设计「Win」Windows程序设计「DSA」数据结构与算法「File」数据文件格式 目录 术语介绍…

【javaweb】学习日记Day1 - HTML CSS入门

目录 一、图片标签 ① 绝对路径 1.绝对磁盘路径 2.绝对网络路径 ② 相对路径 &#xff08;推荐&#xff09; 二、标题标签 三、水平线标签 四、标题样式 1、CSS引入样式 ① 行内样式 ② 内嵌样式 ③ 外嵌样式 2、CSS选择器 ① 元素选择器 ② id选择器 ③…

74HC595驱动7x11点阵屏(LED-7X11-JHM)DEMO

起因 由于我之前做了一个点阵时钟 &#xff0c;但是无奈LED点阵屏价格比较贵&#xff0c;所以想找一个价格较为便宜的点阵来做便宜一点的点阵方案&#xff0c;再淘宝上看到有那种五毛钱一个的7x11的LED点阵&#xff0c;所以就想着试试搞一下这种点阵屏&#xff0c;这个由于是7…

Ajax 笔记(一)—— Ajax 入门

笔记目录 1. Ajax 入门1.1 Ajax 概念1.2 axios 使用1.2.1 URL1.2.2 URL 查询参数1.2.3 小案例-查询地区列表1.2.4 常用请求方法和数据提交1.2.5 错误处理 1.3 HTTP 协议1.3.1 请求报文1.3.2 响应报文 1.4 接口文档1.5 案例1.5.1 用户登录&#xff08;主要业务&#xff09;1.5.2…

SpringBoot案例-部门管理-根据id查询

目录 根据页面原型&#xff0c;明确需求 查看接口文档 思路分析 接口功能实现 控制层&#xff08;Controller类&#xff09; 业务层&#xff08;Service类&#xff09; 业务类 业务实现类 持久层&#xff08;Mapper类&#xff09; 接口测试 前后端联调 根据页面原型&…

JlinkV8 - 8步修复Jlink固件

现象 用着用着Jlink设备可以检测到&#xff0c;但是MDK检测不到设备序列号&#xff0c;换一个Jlink即可正常识别与烧录&#xff0c;很大概率是Jlink固件丢了&#xff0c;我用的山寨版本&#xff0c;市面基本是山寨版本 解决办法 1、查看Jlink的芯片型号&#xff0c;比如我打开…

练习第30天

选择 多线程可以提高CPU利用率 不能提高内存的利用率 A B A C A B A B C 编程 最难的问题最难的问题__牛客网 #include <iostream> #include <string> #include <unordered_map> using namespace std;//给个字符串翻译出来 // int main() {unordered_…

怎么开通Tik Tok海外娱乐公会呢?

TikTok作为全球知名的社交媒体平台&#xff0c;吸引了数亿用户的关注和参与。许多公司和个人渴望通过开通TikTok直播公会进入这一领域&#xff0c;以展示自己的创造力和吸引更多粉丝。然而&#xff0c;成为TikTok直播公会并非易事&#xff0c;需要满足一定的门槛和申请找cmxyci…

小体积,大能量!邂逅飞凌OKMX6ULL开发板

机缘巧合参加了飞凌嵌入式的试用活动&#xff0c;也很幸运被任命为新品体验官&#xff0c;那么看下是哪一款核心板和底板吧。 →核心板&#xff1a;FETMX6ULL-C核心板 FETMX6ULL-C核心板采用NXP i.MX6ULL处理器开发设计&#xff0c;采用低功耗的ARM Cortex-A7架构&#xff0c…

MySQL语句总和之MySQL数据库与表结构操作

目录 1、启动MySQL服务 2、进入MySQL数据库 3、退出数据库 4、查看MySQL数据库所有库 5、创建、删除、使用、查看所处库操作 6、创建表 7、查看表结构 8、表结构操作 1&#xff09;修改表名 2&#xff09;自增长操作 3)添加一个address字段放在Phone字段后面 4)添加…

题34(在排序数组中查找元素的第一个和最后一个位置)

使用二分查找 此题的关键在于找到左端点和右端点 找中点 两种操作 左端点用第一个方式 右端点用第二种&#xff0c;避免死循环 二分模板 class Solution { public:vector<int> searchRange(vector<int>& nums, int target) {if(nums.size()0) return{-…

项目管理师基础之项目管理计划和项目文件

项目管理过程中&#xff0c;会使用并产生两大类文件&#xff1a;项目管理计划和项目文件。内容一般如下&#xff1a; 整个项目生命周期需要收集、分析和转化大量的数据。从各个过程收集项目数据&#xff0c;并在项目团队内共享。在各个过程中所收集的数据经过结合相关背景的分…