Spring 事务

news2025/1/23 4:55:34

目录

一、事务简介

二、在Spring中实现事务 

编程式事务 

声明式事务

三、事务的传播机制


一、事务简介

事务:就是将一组操作封装成为一个整体执行单元,要么全部执行,要么都不执行。

假如事务执行了一半发生了错误就会对已经执行的部分进行回滚操作。

常见的应用场景就是转账事务,转账过程中发生错误就会全部回滚到事务最初的状态。

二、在Spring中实现事务 

在Spring中实现事务有两种方式:

  1. 编程式事务(通过写代码操作事务);
  2. 声明式事务(通过注解开启和提交事务)。

编程式事务 

在编程式事务中包含如下三个重要步骤:

  1. 获取事务;
  2. 提交事务;
  3. 回滚事务;

在编程式事务中,需要用到以下的两个对象:

  • DataSourceTractionManager;
  • TractionDefinition;

实现一个事务实现添加用户到数据库:

首先定义Mapper接口,在该接口中定义增加用户的方法。

@Mapper
public interface UserMapper {
    int add(UserInfo userInfo) ;//添加用户返回受影响的行数
}

在resources文件夹下新建mapper文件夹生成UserMapper.xml文件,其中新增用户的标签如下:

    <insert id="add">
        insert into userinfo(username,password) values (#{username},#{password})
    </insert>

然后在service包下创建UserService类:

@Service
public class UserService {
    @Autowired
    UserMapper userMapper;
    public int add(UserInfo userInfo){
        return userMapper.add(userInfo);
    }

}

controller包下的UserController类的内容如下:

@RestController
public class UserController {
    @Autowired
    private UserService userService;
    @Autowired
    private DataSourceTransactionManager transactionManager;
    @Autowired
    private TransactionDefinition transactionDefinition;
    @RequestMapping("/add")
    public int add(UserInfo userInfo){
        //先对用户名和密码进行非空校验
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername())
           || !StringUtils.hasLength(userInfo.getPassword()) ) {
            return 0;
        }
        //获取事务
        TransactionStatus transaction = transactionManager.getTransaction(transactionDefinition);
        int result = userService.add(userInfo);
        System.out.println(result);
        //回滚事务
        transactionManager.rollback(transaction);
        return result;
    }

}

那么进行访问add:

此处的返回值为1,表示新增了1行,但是对事物进行了回滚操作,就会发现数据库中的内容不变。如果使用transactionManager.commit(transaction)就会对事务进行提交,就会在数据库中看到新插入的内容。

声明式事务

声明式事务相较于编程式事务简单了许多,只需要在用到事务的方法之前添加@Transational注解就可以实现开启事务和提交事务,如果方法执行过程中发生了异常,会自动进行回滚操作。

使用@Transactional注解修饰的方法必须是public,否则无效,并且当@Transactional修饰类时,会对类中所有的public方法有效。

@Transactional注解中可以使用如下的参数:

关于事务的隔离级别: 事务

使用声明式事务实现增加一个用户:

 @Transactional
    @RequestMapping("/add2")
    public int add2(UserInfo userInfo){
        //先对用户名和密码进行非空校验
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername())
                || !StringUtils.hasLength(userInfo.getPassword()) ) {
            return 0;
        }
        int result = userService.add(userInfo);
        return result;
    }

访问add2:

并且在数据库中也增加了该用户:

 那么中间出现异常,再次测试: 

    @Transactional
    @RequestMapping("/add2")
    public int add2(UserInfo userInfo){
        //先对用户名和密码进行非空校验
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername())
                || !StringUtils.hasLength(userInfo.getPassword()) ) {
            return 0;
        }
        int result = userService.add(userInfo);
        int a = 99 / 0;
        return result;
    }

代码中的异常发生在添加之后,在数据中查看:

并没有添加成功,此时事务进行了回滚操作。

在使用@Transactional注解的方法中如果使用try-catch处理了异常之后,此时发生异常就不会进行回滚,就需要在catch语句中手动进行回滚操作:

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

也可以直接抛出异常,不使用try-catch进行处理了。

@Transactional注解的实现原理 

@Transactional注解是基于AOP思想实现的,而AOP又是基于动态代理实现的。

该注解在执行业务之前,会通过代理开启事务,在执行成功之后提交事务,或者遇到异常进行事务回滚。

三、事务的传播机制

事务的传播机制:多个事务相互调用时,事务之间是如何传递的。

事务的传播机制与多事务并发的区别:

 

事务传播机制的类别 

REQUIRED:默认的事务传播级别,如果当前存在事务,就加入该事务,如果当前没有事务就创建一个新的事务(有房子就可以结婚,没房子买房才能结婚)

SUPPORTS:如果当前存在事务,就加入该事务,如果当前没有事务就以非事务的方式运行(有房子就可以结婚,没房子租房也可以结婚)

MANDATORY:如果当前存在事务,就加入该事务,如果当前没有事务就抛出异常(有房子就可以结婚,没房子就分手)

REQUIRES_NEW:创建一个新事务,如果当前存在事务就把当前事务挂起。(不管之前有没有房子都必须买房)

NOT_SUPPORTED:以非事务的方式运行,如果当前存在事务就降档前事务挂起(不管之前有没有房子都必须租房子住)

NEVER:以非事务的方式运行,如果当前存在事务就抛出异常(如果之前有房子就分手)

NESTED:如果当前存在事务就创建一个事务作为当前事务的嵌套事务来运行,如果当前没有事务就创建一个新的事务(如果之前有房子,就把他作为备用再买一套房,如果之前没房,也要买房)

场景演示:

使用REQUIRED的隔离级别:

UserController中的代码:

@Transactional( propagation = Propagation.REQUIRED)
    @RequestMapping("/add3")
    public int add3(UserInfo userInfo){
        //先对用户名和密码进行非空校验
        if(userInfo == null || !StringUtils.hasLength(userInfo.getUsername())
                || !StringUtils.hasLength(userInfo.getPassword()) ) {
            return 0;
        }
        int result = userService.add(userInfo);
        ArticleInfo articleInfo = new ArticleInfo();
        articleInfo.setTitle("test");
        articleInfo.setContent("uihjhj");
        articleInfo.setUid(userInfo.getId());
        articleInfo.setRcount(result);
        articleService.add(articleInfo);
        try {
            int m = 99/0;
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return result;
    }

UserService的代码:

  @Transactional( propagation = Propagation.REQUIRED)
    public int add(UserInfo userInfo){
        return userMapper.add(userInfo);
    }

 ArticleService中的代码:

@Transactional( propagation = Propagation.NESTED)
    public int add(ArticleInfo articleInfo){
        int result = articleMapper.add(articleInfo);
        try {
            int num = 10 / 0;
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return result;
    }

日志显示已经插入数据成功: 

但是访问数据库:

 并没有将想要新增的数据添加进去, 因为REQUIRED隔离级别要求如果当前存在事务,就加入该事务,所以发现异常,事务整个进行回滚。

使用REQUIRED_NEW隔离级别处理同样的代码,日志显示:

数据库显示:

REQUIRED_NEW隔离级别表示如果之前存在事务就将其挂起,然后创建新的事务,所以这两个事务互不干扰,因此插入用户成功。

使用NOT_SUPPORTED注解表示以非事务方式运行也会插入成功,日志显示:

数据库查询:

使用NESTED隔离级别也会插入成功,NESTED隔离级别表示之前的事务作为备用,嵌套事务之所以能够实现部分事务的回滚,是因为事务中有一个保存点,嵌套事务进入之后就相当于新建了一个保存点,回滚时只会回滚到当前的保存点,之前的事务不受影响。 

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

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

相关文章

Linux嵌入式学习之Ubuntu入门(三)用户、用户组及文件权限

系列文章目录 一、Linux嵌入式学习之Ubuntu入门&#xff08;一&#xff09;基本命令、软件安装及文件结构 二、Linux嵌入式学习之Ubuntu入门&#xff08;二&#xff09;磁盘文件介绍及分区、格式化等 文章目录系列文章目录用户与用户组创建用户和用户组图形化创建命令创建文件…

【人工智能】— 逻辑Agent、一般逻辑、Entailment 蕴涵、命题逻辑、前向链接、反向链接、Resolution归结

【人工智能】— 逻辑Agent、逻辑智能体Knowledge bases一个简单的基于知识的智能体一般逻辑Entailment 蕴涵Models模型蕴涵与推理命题逻辑逻辑连接词枚举推理有效性可满足性推导和证明霍恩子句Forward chaining 前向链接Proof of completeness&#xff08;完备性&#xff09;Ba…

QT学习开发笔记(项目实战之智能家居物联网项目1 )

智能家居物联网项目 本章介绍使用 Qt 开发智能家居中的一个物联应用。简单直白的说就是通过云服务器远程控 制设备&#xff08;与设备通信等&#xff09;。本章可以直接做毕设&#xff0c;是毕设物联网项目的一大福音&#xff01;本章将实现远 程点亮开发板 LED 作为一个项目实…

springboot从2.1.3升级到2.3.5后控制台自动输出http请求日志RequestResponseBodyMethodProcessor

springboot从2.1.3升级到2.3.5后控制台自动输出http请求日志RequestResponseBodyMethodProcessor和RequestMappingHandlerMapping推荐使用第二个方案简单 明了 方便 快捷方案一第一步定义TurboFilter第二步配置logback方案二 直接配置logback的配置XML推荐使用第二个方案简单 明…

Insomnia 简单使用方法

文章目录1. 新建工程2. 新建若干文件夹3. 设置环境变量4. 授权以及进行请求的链式调用 (chaining requests)4. 1 解决办法 14. 2 解决办法 2Insomnia 同 Postman&#xff0c; 用于测试后端 endpoint&#xff0c;很容易使用。使用步骤如下&#xff1a;1. 新建工程 2. 新建若干文…

字节跳动软件测试面试过了,起薪20k

普通二本计算机专业毕业&#xff0c;从毕业后&#xff0c;第一份接触测试的工作是在一家通讯小公司&#xff0c;大部分接触的工作是以功能测试为主&#xff0c;一直都是几千块钱工资&#xff0c;还一度被派出差&#xff0c;以及兼职各种产品、运维、运营的活&#xff0c;感觉自…

四十五、docker之nginx手动部署前端项目

nginx的作用&#xff1a; 一、静态文件服务器和反向代理django服务 django框架中的静态文件服务只在开发调试时提供服务&#xff0c;当以生产模式运行时需要将静态文件部署到静态文件服务器上。 1. 收集django项目中的静态文件 在配置文件中配置STATIC_ROOT 我们在我的项目…

如何在低代码平台上构建ERP软件?

ERP软件是企业管理日常运营需求的关键组件。然而&#xff0c;对于许多企业&#xff0c;尤其是资源有限的企业来说&#xff0c;尝试构建和管理ERP平台可能要担负较高的成本的。幸运的是&#xff0c;低代码平台可以使这个过程变得容易得多。今天我们来解释如何在低代码平台上构建…

细粒度软件缺陷预测模型构建方法

在软件缺陷预测领域&#xff0c;粒度指的是缺陷库中每条样本的项目粒度.其中可以分为类粒度、文件粒度或者包粒度等。不同的开发语言有不同的层级的模块粒度&#xff0c;在软件缺陷预测领域&#xff0c;一般来说&#xff0c;在预测效果能够满足要求的情况下&#xff0c;粒度越小…

map容器(重要)

1、map容器简介 Map容器中所有的元素都会根据元素的键值自动实现排序。Map中所有的元素都是pair&#xff0c;pair的简介 同时拥有实值和键值&#xff0c;而前面的 set 只是拥有 键值&#xff0c;pair 中的 第一个元素是键值&#xff0c;而第二个值是 实值。Map 中是不允许有两…

Maven的进阶操作

系列文章目录 Maven进阶操作的学习 文章目录系列文章目录前言一、分模块开发与设计二、依赖管理1.依赖传递2.可选依赖3.排除依赖三、继承与聚合1.聚合2.继承四、属性1.属性2.版本管理五、多环境配置与应用1.多环境开发2.跳过测试六、私服1.私服简介2.私服仓库分类3.资源上传与…

IDEA的基本使用

IDEA的基本使用IDEA的基本使用1 IDEA概述2 IDEA的下载和安装2.1 下载2.2 安装3 IDEA中层级结构介绍3.1 结构分类3.2 结构介绍project&#xff08;项目、工程&#xff09;module&#xff08;模块&#xff09;package&#xff08;包&#xff09;class&#xff08;类&#xff09;3…

C#基础学习--委托

委托 什么是委托 可以认为委托是持有一个或多个方法的对象。 委托和类一样&#xff0c;是一种用户自定义的类型。但类表示的是数据和方法的集合&#xff0c;而委托则持有一个或多个方法&#xff0c;以及一系列预定操作。 可以通过一下擦欧总来使用委托&#xff1a; 可以把d…

度量,我们如何以终为始,以始为终?

你好&#xff0c;我是黄俊彬。 很多时候在研发过程中&#xff0c;我们都习惯性地用“拍脑袋”的方式来看待一个事情。例如这个代码写得不好、这个自动化测试覆盖不充分、版本的发布频率太差了等等。往往只知道哪里有问题&#xff0c;但是却不知如何去找出根因&#xff0c;真正…

14、MyBatis-Plus入门到进阶

1、Mybatis的问题 【简介】&#xff1a; 每个实体类对应一个实体类,对应一个mapper接口,对应一个mapper.xml文件,每个mapper接口都有重复的crud方法,每一个mapper.xml都有重复的crud的sql配置。 2、Mybatis-plus简介 1、官网 Mybatis-plusMybatis-plus 2、MyBatis是什么&a…

JS常用字符串方法

&#x1f4dd;个人主页&#xff1a;爱吃炫迈 &#x1f48c;系列专栏&#xff1a;数据结构与算法 &#x1f9d1;‍&#x1f4bb;座右铭&#xff1a;道阻且长&#xff0c;行则将至&#x1f497; 文章目录移除字符串首尾空白符trim()trimStart()trimEnd()获取字符串指定位置的值ch…

记2023第十四届蓝桥杯感受

弱校ACM队员&#xff0c;曾获CB国二&#xff0c;CG国优第六。&#xff08;我是fw&#xff09; ------------------------------------------------------------------------分割线 2023.04.09 今年再次参加CG组&#xff0c;估计g了&#xff0c;盲猜一波省二前排&#xff0c;出…

基于BenchmarkSQL的Oracle数据库tpcc性能测试

基于BenchmarkSQL的Oracle数据库tpcc性能测试安装BenchmarkSQL及其依赖安装软件依赖编译BenchmarkSQLBenchmarkSQL props文件配置数据库用户配置BenchmarkSQL压测装载测试数据TPC-C压测&#xff08;固定事务数量&#xff09;TPC-C压测&#xff08;固定时长&#xff09;生成测试…

C++轻量级Web服务器TinyWebServer源码分析之http篇

文章目录http篇简介一、服务器接收http请求和http报文解析解析报文整体流程从状态机逻辑主状态机逻辑二、服务器响应请求报文http篇简介 http类这篇个人觉得是最难同时也是最繁琐的一篇&#xff0c;本篇在基础知识方面&#xff0c;包括epoll、HTTP报文格式、状态码和有限状态机…

Python 小型项目大全 71~75

七十一、声音模拟 原文&#xff1a;http://inventwithpython.com/bigbookpython/project71.html 类似于西蒙电子玩具&#xff0c;这款识记游戏使用第三方playsound模块&#xff0c;播放四种不同的声音&#xff0c;分别对应键盘上的A、S、D、F键。当你成功地重复游戏给你的图案时…