Spring数据库事务处理

news2024/11/19 2:33:37

数据库事务的基本知识

ACID

在这里插入图片描述

两类丢失更新

事务回滚丢失更新:

在这里插入图片描述
目前大部分数据库已经通过锁的机制来避免了事务回滚丢失更新。
数据库锁的机制:
锁可以分为乐观锁和悲观锁,而悲观锁又分为:读锁(共享锁)和写锁(排它锁),而数据库实现了悲观锁中的读锁和写锁,而乐观锁则需要开发人员自己实现。

数据库在设计这两种锁的时候,这两种锁间的关系如下:读锁与读锁可以共存,读锁与写锁互斥,写锁与写锁互斥。

比如说,当a操作某条数据时,数据库就会给这条数据加锁,其他人只能查看这条数据,但是却不能操作,只有当a事务提交结束以后,锁被取消了,其他人才可以修改这条数据。

事务提交丢失更新

在这里插入图片描述
这是高并发编程中需要重点关注的问题,数据库为了压制此类丢失更新,提出了事务之间的隔离级别的概念。

数据库事务的隔离级别

未提交读

允许一个事务读取到另一个事务没有提交的数据。
这种隔离级别可以拥有很好的并发能力,但是对于数据的一致性无法保证,所以适用于追求高并发性,但是对数据一致性要求低的场景。
另外,未提交读的隔离级别会造成脏读的现象:
在这里插入图片描述

读写提交

一个事务只能读取另一个事务已经提交的数据,而不能读取未提交的数据。
这种隔离级别可以避免脏读的发生。
在这里插入图片描述
虽然读写提交的隔离级别克服了脏读的发生,但是又会出现不可重复读的现象:
在这里插入图片描述

可重复读

可重复读的隔离级别是为了克服不可重复读的问题:
在这里插入图片描述
可重复读的隔离级别虽然克服了不可重复读的问题,但是会引入幻读的问题:
在这里插入图片描述
幻读和可重复读的区别:
幻读是针对于统计的场景,而可重复读是针对于一条数据而言的。

串行化

为了解决上述的各种问题,数据库提出了串行化的隔离级别。
这种级别下,所有的sql都会按照顺序执行,可以完全保证数据的一致性。

合理使用数据库隔离级别

在这里插入图片描述
虽然串行化可以解决脏读、不可重复读、幻读等问题,但是它也有一个很显著的特点,就是并发能力低下,这四种隔离级别的并发能力排名如下:
未提交读 > 读写提交 > 可重复读 > 串行化
所以使用时需要根据具体的业务场景来权衡使用。

另外,不同数据对于隔离级别的支持也是不一样的,比如:
Oracle:读写提交、串行化
MySQL:未提交读、读写提交、可重复读、串行化
PG:读写提交、可重复读、串行化

Spring数据库事务简介:

在 Spring 中,事务管理器的顶层接口为PlatformTransactionManager,Spring 还为此定义了一系列的接口和类,它们之间的关系如图所示:
在这里插入图片描述
当我们引入其它框架时,还会有其它的事务管理器的类,比方说我们引入 Hibernate ,那么 Spring还会提供HibernateTransactionManager 与之对应并给我们使用 。这里我们以 MyBatis 框架为例,去讨论Spring 数据库事务方面的问题,最常用到的事务管理器是 DataSourceTransactionManager 。从上图中可以看到它也是一个实现了接口 PlatfonnTransactionManager 的类。

PlatfonnTransactionManager接口的源码:

下面我们看一下PlatfonnTransactionManager接口的源码:

public interface PlatformTransactionManager {
	// 获取事务
	TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
	// 事务提交
	void commit(TransactionStatus status) throws TransactionException;
	// 事务回滚
	void rollback(TransactionStatus status) throws TransactionException;
}

可以看到它里面定义了获取事务、提交事务、回滚事务的方法。而mybatis的事务管理器DataSourceTransactionManager实现了PlatformTransactionManager 接口,所以它也拥有了这些方法。

事务的传播行为

所谓传播行为,就是方法之间调用时,事务采取的策略。

在Spring的事务机制中,对于数据库而言存在7中传播行为,它们都被定义在Propagation枚举中,该枚举源码如下:

public enum Propagation {
	// 需要事务,它也是默认的传播行为
	// 如果当前存在事务,就加入到当前事务中一起运行
	// 如果当前不存在事务,就新建一个事务来运行方法
	// 使用频次高
	REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

	// 支持事务
	// 如果当前存在事务,就加入到当前事务中一起运行
	// 如果不存在,就继续采用无事务的方式运行
	SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

	// 必须使用事务
	// 如果当前存在事务,就加入到当前事务中一起运行
	// 如果不存在事务,就抛出异常
	MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

	// 无论当前是否存在事务,都会创建一个新的事务来运行
	// 使用频次高
	REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),

	// 不支持事务
	// 如果当前存在事务,将挂起事务运行
	NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

	// 不支持事务
	// 如果存在事务,就抛出异常
	// 如果不存在,就继续采用无事务的方式运行
	NEVER(TransactionDefinition.PROPAGATION_NEVER),

	// 事务嵌套
	// 当前方法调用子方法时,如果子方法出现异常,则只回滚子方法执行过的sql,不会回滚当前方法的事务
	// 使用频次高
	NESTED(TransactionDefinition.PROPAGATION_NESTED);
}

REQUIRED、REQUIRES_NEW、NESTED这三种使用频次较高,需要重点关注。

@Transactional注解

Spring对于事务的处理主要采用声明式事务的方式,也就是通过@Transactional注解来进行数据库事务的管理。
下面是它的源码:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
	// 通过bean的name来指定事务管理器
	@AliasFor("transactionManager")
	String value() default "";

	// 和value属性一样
	@AliasFor("value")
	String transactionManager() default "";

	// 设置事务传播行为
	Propagation propagation() default Propagation.REQUIRED;

	// 设置事务隔离级别
	Isolation isolation() default Isolation.DEFAULT;

	// 指定超时时间(单位:秒)
	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

	// 是否只读事务
	boolean readOnly() default false;

	// 方法在发生指定异常时进行回滚,默认是所有异常都会回滚
	Class<? extends Throwable>[] rollbackFor() default {};

	// 方法在发生指定异常名称时进行回滚,默认是所有异常都会回滚
	String[] rollbackForClassName() default {};

	// 方法在发生指定异常时不进行回滚,默认是所有异常都会回滚
	Class<? extends Throwable>[] noRollbackFor() default {};
	
	// 方法在发生指定异常名称时不进行回滚,默认是所有异常都会回滚
	String[] noRollbackForClassName() default {};
}

可以看到,我们在使用@Transactional注解时,是可以自己设置事务的隔离级别、传播行为、回滚机制等等。

@Transactional实战

下面让我们一起实际应用下@Transactional对于数据库事务的控制。
首先我们创建一个保存数据库的,代码如下:

REQUIRED传播行为测试

@Service
@Slf4j
public class UserServiceImpl implements IUserService {
	......
	
	@Autowired
    @Lazy
    private IUserService userService;
    
	@Override
    @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public int insert(User user) {
        long oldId = user.getId();
        user.setId(null);
        userMapper.insert(user);
        if(oldId == 10){
            throw new IllegalArgumentException("事务回滚测试" + user.getId());
        }
        user.setUpdatedBy(10288931L);
        return userMapper.update(user);
    }

	@Override
    public boolean save(int times) {
        for(int timesTmp = 1; timesTmp <= times; timesTmp++){
            User user = easyRandom.nextObject(User.class);
            user.setId((long) timesTmp);
            try {
                userService.insert(user);
            }catch (Exception e){
                log.warn("出现了异常:{}", e.getMessage());
            }
        }
        return true;
    }
}

可以看到,我们将方法insert的事务传播行为设置成了REQUIRED,也就是:
// 需要事务,它也是默认的传播行为
// 如果当前存在事务,就加入到当前事务中一起运行
// 如果当前不存在事务,就新建一个事务来运行方法
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

注意:这里调用insert方法时需要通过代理对象调用,因为Spring对于事务的处理是基于AOP实现的,所以如果不通过代理对象调用,就无法触发事务,也就是事务会失效。

然后将日志级别设置为DEBUG,这样就可以看到事务相关的日志了:

logging.level.root=DEBUG
logging.level.org.springframework=DEBUG
logging.level.org.org.mybatis=DEBUG

按照REQUIRED传播行为的特点,此时调用它的save方法是没有事务的,所以insert方法会单独创建自己的事务来运行,然后我们创建测试用的mapper和controller(这两个就省略了,写法都很简单),调用接口,可以看到日志如下:
在这里插入图片描述
可以看到我新增了5个user信息,日志中也是给insert方法创建了5个事务来运行,这和REQUIRED传播行为的特点是一致的。

此时我们给save方法也加上日志,如下所示:
@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)

这个时候,对于insert方法来说,就是有事务的环境了,按照REQUIRED传播行为的特点,insert方法就不会自己单独创建事务了,而是沿用save方法的事务,我们再次调用接口,得到日志如下:

在这里插入图片描述
可以看到只是给save方法创建了事务,并没有给insert方法单独创建事务
在这里插入图片描述
可以看到insert方法运行时,是加入到原有的事务之中的,这和REQUIRED传播行为的特点是一致的。

REQUIRES_NEW传播行为测试

现在我们将insert方法的事务传播行为设置为REQUIRES_NEW,其特点如下所示:
// 无论当前是否存在事务,都会创建一个新的事务来运行

接下来我们再调用一下接口,得到如下日志:
在这里插入图片描述
可以看到这里是创建了6个事务,其中有一个是save方法的事务,其他5个都是insert方法自己的事务。这和REQUIRED传播行为的特点是一致的。

NESTED传播行为测试

// 事务嵌套
// 如果没有事务,则创建一个自己的事务
// 如果当前有事务,则创建一个嵌套的事务
// 当前方法调用子方法时,如果子方法出现异常,则只回滚子方法执行过的sql,不会回滚当前方法的事务

更改insert方法的事务如下:
@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.NESTED, rollbackFor = Exception.class)

同时去掉save方法的事务,再次调用接口,得到如下日志:
在这里插入图片描述
此时save方法没有添加事务,所以按照NESTED传播行为的特点,insert方法会创建自己的事务。

然后我们再将save方法添加上事务,再次调用接口,得到日志如下:
在这里插入图片描述
在这里插入图片描述
因为此时save方法添加了事务,所以insert方法会创建一个嵌套在save方法里面的事务。

有个注意点:对于嵌套事务来说,当前方法调用子方法时,如果子方法出现异常,则只回滚子方法执行过的sql,不会回滚当前方法的事务,这个效果是通过数据库的保存点技术来实现的,至于数据库的保存点技术,可以自行了解一下。

好了,今天就到这里了,感兴趣的小伙伴赶紧去试试吧,拜拜。

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

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

相关文章

chatgpt赋能python:使用Python制作动画的步骤和工具

使用Python制作动画的步骤和工具 Python不仅是一种流行的编程语言&#xff0c;还可以用于制作动画。本文将介绍一些基本步骤和工具&#xff0c;帮助你制作出精美的动画作品。 步骤一&#xff1a;选择一个好的动画框架 Python有很多强大的动画框架可以使用。其中一些最受欢迎…

chatgpt赋能python:Python删除目录下文件:介绍和方法

Python删除目录下文件&#xff1a;介绍和方法 在日常的开发和运维工作中&#xff0c;我们可能会需要删除某个目录下的一些文件。使用Python的好处在于其强大的处理能力和简单易用的语法。本文将介绍Python删除目录下文件的方法&#xff0c;并提供一些实用的代码示例。 使用os…

java-数组和方法

java-数组和方法 一、数组 1.1 概念 数组就是存储数据长度固定的容器&#xff0c;存储多个数据的数据类型要一致。 1.2 定义格式 第一种 数据类型 [] 数组名 例: int[] arr; double[] arr; char[] arr;第二种 数据类型 数组名[] 例: int arr[]; doubl…

交流有功功率、无功功率、视在功率计算

交流有功功率、无功功率、视在功率计算 1、有功功率2、无功功率3、视在功率4、功率因数5、总结 1、有功功率 有功功率&#xff1a;在交流电路中&#xff0c;每个瞬时的有功功率是不同的&#xff0c;且不断变化&#xff0c;一般用平均有功功率&#xff08;一个周期内功率的平均…

黑盒测试用例设计练习题

1、准考证号码 对招干考试系统“输入学生成绩”子模块设计测试用例。招干考试分三个专业,准考证号第一位为专业代号&#xff0c;如&#xff1a; 1-行政专业&#xff0c; 2-法律专业&#xff0c; 3-财经专业。 行政专业准考证号码为&#xff1a;110001&#xff5e;111215 法律专…

chatgpt赋能python:Python数据清洗:如何剔除异常值

Python 数据清洗&#xff1a;如何剔除异常值 随着数据量的持续增长&#xff0c;数据清洗已经成为了数据分析中不可或缺的一个步骤。数据清洗的目的是去除错误、缺失或不必要的数据&#xff0c;从而使数据更加准确、可靠、一致和完整。本文将讲解如何使用 Python 编程语言来剔除…

【C++ 程序设计】第 2 章:面向对象的基本概念

目录 一、结构化程序设计 二、面向对象程序设计的概念和特点 &#xff08;1&#xff09;面向对象程序设计的概念 &#xff08;2&#xff09;面向对象程序设计的特点 三、类的初步知识 &#xff08;1&#xff09;类的定义 &#xff08;2&#xff09;类的定义示例 四、类…

Three.js--》实现3d踢球模型展示

目录 项目搭建 初始化three.js基础代码 设置环境纹理加载模型 使用Cannon-es实现物理世界 今天简单实现一个three.js的小Demo&#xff0c;加强自己对three知识的掌握与学习&#xff0c;只有在项目中才能灵活将所学知识运用起来&#xff0c;话不多说直接开始。 项目搭建 本…

Linux4.6LNMP架构

文章目录 计算机系统5G云计算第五章 LINUX LNMP架构一、安装 Nginx 服务1.关闭防火墙&#xff0c;将安装nginx所需软件包传到/opt目录下2.安装依赖包3.创建运行用户、组4.编译安装Nginx5.添加 Nginx 系统服务6.修改nginx服务配置文件 三、编译安装mysqld 服务1.将安装mysql 所需…

MySQL—存储引擎(下)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a;小刘主页 ♥️每天分享云计算网络运维课堂笔记&#xff0c;努力不一定有回报&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️树高千尺&#xff0c;落叶归根人生不易&…

Windows部署WSL开启pwn之旅

不用虚拟机来操作真的太爽了&#xff01;&#xff01;还有和vscode的联动&#xff0c;更是妙绝&#xff01; 目录 前言 一、WSL获取 开启WSL支持 二、各种操作 1.apt 换源 2.更新软件 3.配置SSH服务器&#xff08;对于pwn来说非必要&#xff09; 4.WSL访问windows文件 …

【ChatGPT 】国内无需注册 openai 即可访问 ChatGPT:WeTab 浏览器扩展程序的安装与使用

目录 一、Sider&#xff1a;每天免费 30 查询额度【暂不推荐】 &#xff08;1&#xff09;Edge 浏览器扩展程序&#xff1a;Sider - ChatGPT【暂不推荐】 &#xff08;2&#xff09;iOS 手机 APP&#xff1a;Sider- Al 搭档&#xff08;有需要可以安装&#xff09; 二、 We…

chatgpt赋能python:Python创建画布的教程

Python 创建画布的教程 Python是一个功能强大的编程语言&#xff0c;其中一个重要的应用是数据可视化。在数据科学、机器学习、图像处理和软件开发中&#xff0c;Python的可视化功能非常实用。本教程将介绍Python创建画布的步骤&#xff0c;以及如何使用Matplotlib创建简单的图…

生产案例:消息堆积排查分析

&#x1f4dd;作者简介&#xff1a; 大家好&#xff0c;我是CBeann&#xff0c;CSDN博客专家&#xff0c;阿里云专家博主。 22届校招进入阿里广告部门从事Java开发工程师。 平时有空会帮大家解决问题&#xff0c;模式面试和日常答疑&#xff0c;并且提供免费云服务器使用。 有一…

2023年06月IDE流行度最新排名

点击查看最新IDE流行度最新排名&#xff08;每月更新&#xff09; 2023年06月IDE流行度最新排名 顶级IDE排名是通过分析在谷歌上搜索IDE下载页面的频率而创建的 一个IDE被搜索的次数越多&#xff0c;这个IDE就被认为越受欢迎。原始数据来自谷歌Trends 如果您相信集体智慧&am…

Linux4.5Nginx网站服务

文章目录 计算机系统5G云计算第五章 LINUX Nginx网站服务一、编译安装Nginx服务1.关闭防火墙&#xff0c;将安装nginx所需软件包传到/opt目录下2.安装依赖包3.创建运行用户、组4.编译安装Nginx5.检查、启动、重启、停止 nginx服务6.添加 Nginx 系统服务 二、认识Nginx服务的主配…

【数据结构】---几分钟简单几步学会手撕链式二叉树(下)

文章目录 前言&#x1f31f;一、二叉树链式结构的实现&#x1f30f;1.1 二叉树叶子节点个数&#x1f4ab;代码&#xff1a;&#x1f4ab;流程图&#xff1a; &#x1f30f;1.2 二叉树的高度&#x1f4ab;第一种写法(不支持)&#xff1a;&#x1f4d2;代码&#xff1a;&#x1f…

​Memcached 架构

Memcached是一种内存中的键值存储&#xff0c;最初是用Perl编写的&#xff0c;后来重写为C语言。它受到Facebook、Netflix和Wikipedia等公司的欢迎&#xff0c;因为它简单易用。 虽然当谈论到软件描述时&#xff0c;“简单”这个词已经失去了意义&#xff0c;但我认为Memcache…

chatgpt赋能python:用Python制作AI:优化搜索引擎结果的关键

用Python制作AI&#xff1a;优化搜索引擎结果的关键 搜索引擎正成为我们日常生活不可或缺的一部分。无论是寻找答案、娱乐还是购物&#xff0c;大多数人都会先打开搜索引擎。随着越来越多的数据被放入互联网中&#xff0c;如何让搜索引擎结果与用户的搜索意图相符&#xff0c;…

chatgpt赋能python:用Python办公自动化轻松完成繁琐重复的工作

用Python办公自动化轻松完成繁琐重复的工作 随着科技的进步&#xff0c;许多传统工作已经被自动化取代。而在许多职业中&#xff0c;办公自动化通常被认为是节省时间和减少错误的最佳方法。对于那些使用Microsoft Office&#xff0c;例如Excel、Word和PowerPoint等应用程序的用…