当前位置: 首页 > news >正文

Spring事务和事务传播机制(1)

前言🍭

❤️❤️❤️SSM专栏更新中,各位大佬觉得写得不错,支持一下,感谢了!❤️❤️❤️

Spring + Spring MVC + MyBatis_冷兮雪的博客-CSDN博客

在Spring框架中,事务管理是一种用于维护数据库操作的一致性和完整性的机制。Spring事务管理提供了灵活的方式来处理事务,包括事务的创建、提交、回滚以及事务的传播行为。

一、为什么需要事务?🍭

事务定义:

将一组操作封装成一个执行单元(封装到⼀起),要么全部成功,要么全部失败。

为什么要用事务?

比如转账分为两个操作:

第一步操作:A 账户 -100 元。

第二步操作:B 账户 +100 元。

如果没有事务,第一步执行成功了,第二步执行失败了,那么 A 账户平白无故的 100 元就“人间蒸 发”了。而如果使用事务就可以解决这个问题,让这⼀组操作要么⼀起成功,要么⼀起失败。

二、Spring 中事务的实现🍭

Spring 中的事务操作分为两类:

  1. 编程式事务(手动写代码操作事务)。
  2. 声明式事务(利用注解自动开启和提交事务)。

在开始讲解它们之前,咱们先来回顾事务在 MySQL 中是如何使用的。

1、MySQL 中的事务使用🍉

事务在 MySQL 有 3 个重要的操作:开启事务、提交事务、回滚事务,它们对应的操作命令如下:

-- 开启事务
start transaction;
-- 业务执行
-- 提交事务
commit;
回滚事务
rollback;

2、Spring 编程式事务(了解)🍉

Spring 手动操作事务和上面MySQL 操作事务类似,它也是有 3 个重要操作步骤:

  • 开启事务(获取事务)。
  • 提交事务。
  • 回滚事务。

SpringBoot 内置了两个对象,DataSourceTransactionManager 用来获取事务(开启事务)、提交或回滚事务的,而TransactionDefinition 是事务的属性,在获取事务的时候需要将 TransactionDefinition 传递进去从而获得⼀个事务 TransactionStatus,实现代码如下:

package com.example.demo.controller;import com.example.demo.entity.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.time.LocalDateTime;@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@Autowiredprivate DataSourceTransactionManager transactionManager;@Autowiredprivate TransactionDefinition transactionDefinition;@RequestMapping("/add")public int add(UserInfo userInfo){// 非空效验if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername())|| !StringUtils.hasLength(userInfo.getPassword())) {return 0;}// 1.开始事务TransactionStatus transactionStatus =transactionManager.getTransaction(transactionDefinition);// 手动设置创建时间和修改时间的默认值userInfo.setCreatetime(LocalDateTime.now().toString());userInfo.setUpdatetime(LocalDateTime.now().toString());int result = userService.add(userInfo);System.out.println("添加:" + result);// 2.回滚事务transactionManager.rollback(transactionStatus);return result;}
}

因为回滚了事务,所以数据库中不会有wangwu这个用户。

从上述代码可以看出,以上代码虽然可以实现事务,但操作也很繁琐,有没有更简单的实现方法呢?请看下面声明式事务。

3、Spring 声明式事务(自动)🍉

声明式事务的实现很简单,只需要在需要的方法上添加 @Transactional 注解就可以实现了,无需手动开启事务和提交事务,进入方法时自动开启事务,方法执行完会自动提交事务,如果中途发生了没有处理的异常会自动回滚事务,具体实现代码如下:

@Transactional// 声明式事务(自动提交)@RequestMapping("/insert")public Integer insert(UserInfo userInfo) {// 非空效验if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||!StringUtils.hasLength(userInfo.getPassword())) {return 0;}// 添加用户int result = userService.add(userInfo);return result;}

接下里使用以下代码,分别设置 @Transactional 注解和不设置 @Transactional,观察它们的执行区别:

如果添加了 @Transactional注解就不会添加用户,因为程序报错了,它会自动回滚。如果没有@Transactional注解,就会添加用户,然后给前端报错,这是非常危险的。

Ⅰ、@Transactional 作用范围 🍓

@Transactional 可以用来修饰方法或类:

  • 修饰方法时:需要注意只能应用到 public 方法上,否则不生效。推荐此种用法。
  • 修饰类时:表明该注解对该类中所有的 public 方法都生效。

Ⅱ、@Transactional参数说明🍓

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

Ⅲ、注意事项🍓

@Transactional 在异常被捕获的情况下,不会进行事务自动回滚,验证以下代码是否会发生事务回滚:

@Transactional// 声明式事务(自动提交)@RequestMapping("/insert")public Integer insert(UserInfo userInfo) {// 非空效验if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||!StringUtils.hasLength(userInfo.getPassword())) {return 0;}// 添加用户int result = userService.add(userInfo);try {int num=10/0;} catch (Exception e) {System.out.println(e.getMessage());}return result;}

 数据库中也有了wangwu:

事务并没有进行回滚。

事务不会自动回滚解决方案🍓

①解决方案1🍒

对于捕获的异常,事务是会自动回滚的,因此解决方案1就是可以将异常重新抛出,具体实现如下:

@Transactional// 声明式事务(自动提交)@RequestMapping("/insert")public Integer insert(UserInfo userInfo) {// 非空效验if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||!StringUtils.hasLength(userInfo.getPassword())) {return 0;}// 添加用户int result = userService.add(userInfo);try {int num=10/0;} catch (Exception e) {System.out.println(e.getMessage());throw e;}return result;}

②解决方案2🍒

手动回滚事务,在方法中使用TransactionAspectSupport.currentTransactionStatus() 可 以得到当前的事务,然后设置回滚方法 setRollbackOnly 就可以实现回滚了,具体实现代码如下:

@Transactional// 声明式事务(自动提交)@RequestMapping("/insert")public Integer insert(UserInfo userInfo) {// 非空效验if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) ||!StringUtils.hasLength(userInfo.getPassword())) {return 0;}// 添加用户int result = userService.add(userInfo);try {int num=10/0;} catch (Exception e) {System.out.println(e.getMessage());/*throw e;*/TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return result;}

Ⅳ、@Transactional 工作原理🍓

@Transactional 是基于 AOP 实现的,AOP又是使用动态代理实现的。如果目标对象实现了接口,默认情况下会采用JDK 的动态代理,如果目标对象没有实现了接口,会使用CGLIB 动态代理。

@Transactional 在开始执行业务之前,通过代理先开启事务,在执行成功之后再提交事务。如果中途遇到的异常,则回滚事务。

@Transactional 实现思路预览:

@Transactional 具体执行细节如下图所

Spring事务、事务隔离级别、事务传播机制(spring事务的实现方式和原理以及隔离级别?) | 半码博客

三、事务隔离级别🍭

1、事务特性🍉

事务有4 ⼤特性(ACID),原子性、持久性、⼀致性和隔离性,具体概念如下:

  • 原子性:⼀个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过⼀样。
  • ⼀致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
  • 持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
  • 隔离性:数据库允许多个并发事务同时对其数据进⾏读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不⼀致。事务隔离分为不同级别,包括读未提交 (Read uncommitted)、读提交 (read committed)、可重复读 (repeatable read) 和串行化 (Serializable)。
上⾯ 4 个属性,可以简称为ACID。
原⼦性(Atomicity,或称不可分割性)
⼀致性(Consistency)
隔离性(Isolation,⼜称独立性)
持久性(Durability)。

而这 4 种特性中,只有隔离性(隔离级别)是可以设置的。

为什么要设置事务的隔离级别?
设置事务的隔离级别是用来保障多个并发事务执行更可控,更符合操作者预期的。
什么是可控呢?
比如近几年比较严重的新冠病毒,我们会把直接接触到确证病例的人员隔离到酒店,而把间接接触者(和直接接触着但未确诊的人)隔离在自己的家中,也就是针对不同的人群,采取不同的隔离级别,这种隔离方式就和事务的隔离级别类似,都是采取某种行动让某个事件变的“更可控”。而事务的隔离级别就是为了防止,其他的事务影响当前事务执行的一种策略。

http://www.lryc.cn/news/131039.html

相关文章:

  • 如何快速在vscode中实现不同python文件的对比查看
  • 网络安全---Ring3下动态链接库.so函数劫持
  • leetcode283. 移动零
  • GuLi商城-前端基础Vue-生命周期和钩子函数
  • 输入输出+暴力模拟入门:魔法之树、染色の树、矩阵、字母加密、玫瑰鸭
  • ​Kubernetes的演变:从etcd到分布式SQL的过渡
  • 29、简单通过git把项目远程提交到gitee
  • 元宇宙之应用(04)沉浸式游戏
  • 浙大数据结构第八周之08-图7 公路村村通
  • SpringBoot 解决跨域问题
  • 2023 年牛客多校第十场题解
  • 韦东山老师 RTOS 入门课程(一)RTOS 介绍,熟悉裸机的汇编逻辑
  • WebRTC | SDP详解
  • Springboot 实践(9)springboot集成Oauth2.0授权包,5个接口文件配置详解
  • 最新AI系统ChatGPT程序源码/支持GPT4/自定义训练知识库/GPT联网/支持ai绘画(Midjourney)+Dall-E2绘画/支持MJ以图生图
  • 【高频面试题】 消息中间件
  • 物联网智慧安防实训综合实训基地建设方案
  • openGauss学习笔记-44 openGauss 高级数据管理-存储过程
  • 【Linux】进程信号篇Ⅲ:可重入函数、volatile关键字、SIGCHLD信号
  • 排序算法:冒泡排序
  • Spring事件监听源码解析
  • Cpp学习——list的模拟实现
  • 工具推荐:Chat2DB一款开源免费的多数据库客户端工具
  • C语言刷题指南(二)
  • [C++11]
  • 【MySQL系列】--初识数据库
  • Unity导入google.protobuf失败,无法找到google命名空间
  • 使用IDM下载视频出现“由于法律原因,IDM无法下载...
  • pointnet C++推理部署--tensorrt框架
  • 34.Netty源码之Netty如何处理网络请求