Spring:深入理解 Spring 事务原理

Spring 事务

  Spring 框架支持两种主要的事务管理方式:编程式事务(Programmatic Transaction Management)和声明式事务(Declarative Transaction Management)。

1.编程式事务
  编程式事务管理指的是在代码中显式地控制事务的边界和生命周期。这通常意味着你需要手动调用事务管理的 API 来开始、提交或回滚事务。Spring 提供了 PlatformTransactionManager 接口作为编程式事务管理的基础。开发者需要手动编写代码来调用这个接口的方法,例如 getTransaction(), commit(), rollback() 等。
  不过,由于编程式事务管理需要开发者在代码中显式地管理事务,这增加了代码的复杂性,并且可能导致错误(比如忘记提交或回滚事务)。因此,编程式事务管理在 Spring 应用中不太常用。

编程式事务示例

@Service  
public class MyService {  private final PlatformTransactionManager transactionManager;  @Autowired  public MyService(PlatformTransactionManager transactionManager) {  this.transactionManager = transactionManager;  }  public void executeInTransaction() {  DefaultTransactionDefinition def = new DefaultTransactionDefinition();  // 设置隔离级别、传播行为等(如果需要)  // def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);  TransactionStatus status = transactionManager.getTransaction(def);  try {  // 在这里执行数据库操作...  // 如果需要回滚,可以调用 transactionManager.rollback(status);  // 如果一切顺利,提交事务 transactionManager.commit(status);  } catch (Exception ex) {  transactionManager.rollback(status);  throw ex;  }  }  
}在使用编程式事务处理的过程中,利用DefaultTransactionDefinition对象来持有事务处理属性。同时,在创建事务的过程中得到一个TransactionStatus对象,然后通过直接调用transactionManager对象 的commit()rollback()方法来完成事务处理。

2.声明式事务
  声明式事务管理则是一种更为方便和推荐的方式。开发者通过注解(如 @Transactional)或 XML 配置来声明哪些方法需要事务支持,而不需要在代码中显式地管理事务。Spring 容器会自动为这些方法添加事务管理逻辑。
  在 Spring 中,声明式事务管理通常是通过 AOP(面向切面编程)来实现的。Spring 容器在运行时为需要事务支持的方法创建一个代理对象,当这些方法被调用时,代理对象会自动添加事务管理逻辑。
  使用 @Transactional 注解是最常见的声明式事务管理方式。你可以将 @Transactional 注解添加到类、接口或方法上,以指定哪些方法需要事务支持。此外,你还可以使用 @EnableTransactionManagement 注解来启用声明式事务管理,并通过配置事务管理器(如 DataSourceTransactionManager 或 JpaTransactionManager)来定义事务的属性(如传播行为、隔离级别等)。

@Configuration  
public class AppConfig {  @Autowired  private DataSource dataSource; // 假设已经定义了一个DataSource bean  @Bean  public PlatformTransactionManager transactionManager() {  return new DataSourceTransactionManager(dataSource);  }  
}

Spring事务传播行为

以下是Spring中七种事务传播行为的详细解释:

①.PROPAGATION_REQUIRED(默认)
1.如果当前没有事务,就新建一个事务。
2.如果当前存在事务,就加入这个事务。
这是最常见的选择。

@Service  
public class UserService {  @Autowired  private UserRepository userRepository;  @Transactional  public void createUser(User user) {  userRepository.save(user);  // 其他业务逻辑...  }  
}

②.PROPAGATION_SUPPORTS
1.如果当前没有事务,就以非事务方式执行。
2.如果当前存在事务,就加入这个事务。
  也就是说,方法是否在一个事务中运行是无关紧要的,如果方法运行在事务上下文中,那么它就使用这个事务;否则,方法就作为非事务性代码来执行

@Service  
public class UserService {  @Autowired  private UserRepository userRepository;  @Transactional(propagation = Propagation.SUPPORTS)  public void createUserIfNotInTransaction(User user) {  userRepository.save(user);  // 其他业务逻辑...  }  
}

③.PROPAGATION_MANDATORY
1.如果当前没有事务,就抛出异常。
2.如果当前存在事务,就加入这个事务。
该设置使得事务性上下文必须已经存在,否则,一个事务性异常将被抛出。

@Service  
public class UserService {  @Autowired  private UserRepository userRepository;  @Transactional(propagation = Propagation.MANDATORY)  public void createUserMandatory(User user) {  userRepository.save(user);  // 其他业务逻辑...  }  
}

④.PROPAGATION_REQUIRES_NEW
1.如果当前没有事务,就新建一个事务。
2.如果当前存在事务,把当前事务挂起,并且自己创建一个新的事务给自己使用。
  这个设置总是新建一个事务,并且如果当前已经存在一个事务,就把这个存在的事务挂起起来。也就是说,不管外部方法是否开启事务,Propagation.REQUIRES_NEW修饰的内部方法会起一个新的事务。

@Service  
public class UserService {  @Autowired  private UserRepository userRepository;  @Transactional(propagation = Propagation.REQUIRES_NEW)  public void createUserInNewTransaction(User user) {  userRepository.save(user);  // 其他业务逻辑...  }  
}

⑤.PROPAGATION_NOT_SUPPORTED
1.以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
总是以非事务性方式执行,并挂起任何存在的事务。

@Service  
public class UserService {  @Autowired  private UserRepository userRepository;  @Transactional(propagation = Propagation.NOT_SUPPORTED)  public void createUserWithoutTransaction(User user) {  userRepository.save(user);  // 其他业务逻辑...  }  
}

⑥.PROPAGATION_NEVER
1.以非事务方式执行,如果当前存在事务,则抛出异常。
  这是Propagation.NOT_SUPPORTED的反面设置,它总是以非事务性方式执行,如果存在一个活动事务,则抛出异常。

@Service  
public class UserService {  @Autowired  private UserRepository userRepository;  @Transactional(propagation = Propagation.NEVER)  public void createUserNever(User user) {  userRepository.save(user);  // 其他业务逻辑...  }  
}

⑦.PROPAGATION_NESTED
1.如果当前没有事务,就新建一个事务。
2.如果当前存在事务,则在嵌套事务内执行。
  如果一个活动的事务存在,则运行在一个嵌套的事务中。嵌套的事务可以独立于当前事务进行提交或回滚。如果嵌套事务失败,则它可以被回滚,而外部事务可以继续。

@Service  
public class UserService {  @Autowired  private UserRepository userRepository;  @Transactional(propagation = Propagation.NESTED)  public void createUserNested(User user) {  userRepository.save(user);  // 其他业务逻辑...  }  
}

Spring事务的隔离级别

先来了解一下数据库中的隔离级别
1.读未提交(Read Uncommitted):
值/等级:最低隔离级别。
特性:一个事务可以读取另一个事务未提交的数据。这可能导致脏读、不可重复读和幻读等问题。

2.读已提交(Read Committed)
值/等级:较读未提交更高的隔离级别。
特性:一个事务只能读取另一个事务已经提交的数据。这避免了脏读问题,但在并发情况下,可能会出现不可重复读和幻读。

3.可重复读(Repeatable Read)
值/等级:在某些数据库系统(如MySQL的InnoDB引擎)中是默认的隔离级别。
特性:一个事务在整个过程中可以多次读取同一数据,并且每次读取的结果都相同。这避免了不可重复读和脏读,但在并发情况下,可能会出现幻读。

4.序列化(Serializable)
值/等级:最高的隔离级别。
特性:事务串行执行,避免了脏读、不可重复读和幻读问题。但并发性能较差,通常只在特殊情况下使用。

脏读:一事务对数据进行了增删改,但未提交,另一事务可以读取到未提交的数据。如果第一个事务这时候回滚了,那么第二个事务就读到了脏数据。不可重复读:一个事务中发生了两次读操作,第一次读操作和第二次操作之间,另外一个事务对数据进行了修改,这时候两次读取的数据是不一致的。幻读:第一个事务对一定范围的数据进行批量修改,第二个事务在这个范围增加一条数据,这时候第一个事务就会丢失对新增数据的修改。

总结:
1.隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
2.大多数的数据库默认隔离级别为 Read Commited,比如 SqlServer、Oracle
3.少数数据库默认隔离级别为:Repeatable Read 比如:MySQL InnoDB

  在Spring框架中,事务的隔离级别定义了事务之间如何相互影响,特别是它们如何并发地访问数据库资源。Spring支持五个事务隔离级别,这些级别与JDBC规范中定义的隔离级别相对应。以下是这些隔离级别的详细说明:

①.ISOLATION_DEFAULT(默认隔离级别):
使用数据库默认的隔离级别。这取决于底层数据库系统。

②.ISOLATION_READ_UNCOMMITTED(读取未提交):
这是最低的隔离级别。它允许一个事务读取另一个事务尚未提交的数据。这可能导致几个问题,如脏读(dirty read)、不可重复读(non-repeatable read)和幻读(phantom read)。

③.ISOLATION_READ_COMMITTED(读取已提交):
这是大多数数据库系统的默认隔离级别(但不是所有)。它确保一个事务只能读取另一个事务已经提交的数据。这可以防止脏读,但在高并发情况下,可能会导致不可重复读和幻读。

④.ISOLATION_REPEATABLE_READ(可重复读):
这是MySQL的默认隔离级别(对于InnoDB存储引擎)。它确保在同一个事务中多次读取同一数据时,结果都是一致的。这解决了不可重复读的问题,但仍然可能出现幻读。

⑤.ISOLATION_SERIALIZABLE(可串行化):
这是最高的隔离级别。它通过强制事务串行执行来避免脏读、不可重复读和幻读。但这也意味着它会导致性能下降,因为事务必须等待其他事务完成才能开始。

在Spring中,可以使用@Transactional注解的isolation属性来指定事务的隔离级别。

@Service  
public class MyService {  @Transactional(isolation = Isolation.READ_COMMITTED)  public void myTransactionalMethod() {  // ... 方法体 ...  }  
}
在上面的例子中,myTransactionalMethod方法将在READ_COMMITTED隔离级别下运行。

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

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

相关文章

使用 LangGraph 实现邮件智能处理系统来处理客户咨询【附代码】

简介 在这篇博文中,我们将探讨如何使用 LangGraph 结合 RAG(Retrieval-Augmented Generation)技术来构建一个响应客户邮件的电子邮件智能系统。LangGraph 是一个强大的框架,专为构建和部署基于图的语言应用而设计。它支持复杂的查…

【Linux】centos7安装软件(rpm、yum、编译安装),补充:查找命令的相关文件路径,yum安装mysql

【Linux】技术上,Linux是内核。而术语上,我们通常说的Linux是完整的操作系统,其实称为"Linux发行版",是将Linux内核和应用系统打包,由不同的发行家族发行了不同版本。Linux发行版众多,主要有RedH…

职校智慧校园现状及问题分析

各大中职院校及高职院校是校园信息化的先行者和开拓者,很早就开始注重信息化基础设施建设和信息化人文素养的提升。在过去几年里,随着国家大力发展与扶植职校教育,学校投入相当的经费进行了校园信息通信网络、计算机等基础硬件设备建设&#…

Python3 笔记:help()查看函数的用法

一些不常用的函数或是模块的用法记不清了怎么办? Python的内置函数help()可以查看函数或模块用途的详细说明。 操作方法很简单,直接在help()括号内填写参数,然后运行就可以看到结果了。 举例: help(input) # 查询input()函数的…

RabbitMQ 面试题(一)

1. 简述为什么要使用 RabbitMQ ? 使用 RabbitMQ 的主要原因包括以下几点: 解耦:在复杂的系统中,不同的服务或组件之间往往需要通信和协作。RabbitMQ 作为消息队列,允许这些组件或服务通过发送和接收消息来交互,而无…

CLion 写 Rust 报Project directory `/Users/.../rsheets` does not exist.

每次打开CLion都会看到像下面这样的报错,Project directory /Users/.../rsheets does not exist.,虽然不会影响你写代码,但每次看到还是不舒服,所以研究一下怎么解决。 原因是这样的,每当我们创建一个 Cargo 项目&…

PDF Squeezer for Mac,让PDF压缩更高效

还在为PDF文件过大而烦恼吗?试试PDF Squeezer for Mac吧!它拥有强大的压缩功能,可以快速将PDF文件压缩至更小的体积,让你的文件传输更快捷。同时,它还支持多种压缩方式,满足你的不同需求。赶快下载体验吧&a…

革新机器人任务规划:TREE-PLANNER引领高效、准确的机器人动作生成新趋势

DeepVisionary 每日深度学习前沿科技推送&顶会论文分享,与你一起了解前沿深度学习信息! 引言 任务规划在机器人技术中扮演着至关重要的角色。它涉及到为机器人设计一系列中级动作(技能),使其能够完成复杂的高级任…

C语言基础-枚举类型

C语言中的枚举类型(enum)是一种用户定义的数据类型,它允许为整数常量指定一个名称,使得代码更易于阅读和维护。枚举类型本质上是一种特殊的整数类型,但每个枚举值都有一个名字,而不是直接使用整数。 枚举类…

掌握 Linux Crontab:完整指南与实用案例

一.简介 1. 什么是 Crontab? Crontab 是一个在 Unix 和类 Unix 操作系统上用来定期执行任务的工具。它是从 “cron table” 衍生而来的,“cron” 是一个 Unix 系统自带的后台守护进程,用于定期执行预定的任务或命令,比如备份文件…

电视剧电影原声背景音乐,经典影视配乐片段音效合集

一、素材描述 本套影视配乐素材,大小1.89G,27个压缩文件。 二、素材目录 宰相刘罗锅配乐片段.rar 影视配乐65首.rar 太极张三丰原声.rar 东邪西毒原声配乐15首.rar 东方不败之风云再起配乐24首.rar 东方不败原声配乐16首.rar 电影大话西游原声配…

python绘制圣诞树 如何用python绘制一个圣诞树

欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~ 目录 一.前言 二.代码 三.使用 四.总结 一.前言 圣诞树,是指用灯烛和装饰品把枞树或洋松装点起来的常青树。作为是圣诞节重要的组成元素之一,近代圣诞树起源于德国,后来逐步在世界范围内流行起来

聊聊Mysql锁问题

聊聊Mysql锁问题 一、 Mysql锁概述二、Myisam锁问题2. 1 表级锁的锁模式2.2 如何加表锁2.3 并发插入2.4 Myisam的锁调度 三、InnoDB锁问题3.1 事务及ACID属性3.2 并发事务处理带来的问题3.3 事务隔离级别3.4 获取InnoDB行锁争用情况3.5 InnoDB行锁模式及加锁方法3.6 InnoDB行锁…

sql-行转列(转置)

- 行转列的常规做法是,group bysum(if())【或count(if())】 例题: 已知 yearmonthamount199111.1199121.2199131.3199141.4199212.1199222.2199232.3199242.4 查成这样一个结果 yearm1m2m3m419911.11.21.31.419922.12.22.32.4 解答: us…

串转并74hC595控制流水灯

#include <reg51.h> //重新定义系统数据类型 typedef unsigned int u16; typedef unsigned char u8; //宏定义数据端口 #define LEDDZ_COL_PORT P0 #define SER_AP_PORT P1 //定义数据存储端口 sbit SERP3^4; sbit SRCLKP3^6; sbit RCLKP3^5; //依次闪烁…

WPS表格:对比少于1万的两列数据

当我们需要对于A、B两列乱序的数据&#xff0c;找出A列中某一项B列有没有&#xff0c;或者找出B列中的某一项A列有没有&#xff0c;都可以先将这两列数据放入WPS表格中&#xff1a; 1.选中C列的第一行的单元格&#xff0c;在函数区输入函数 如果我们以A为基准&#xff0c;找A中…

SQL语句优化技巧

目录 1、sql语句规范 2、sql语句优化 1、sql语句规范 MySQL在Linux系统下数据库名&#xff0c;表名&#xff0c;存储过程名&#xff0c;函数名称&#xff0c;触发器名称等区分大小写&#xff0c;列名不区分大小写&#xff0c;原因是这些操作系统下文件名称区分大小写。 MySQL…

19、案例实战:上亿请求轻松应对,老年代垃圾回收参数调整技巧大公开

19.1、前文回顾 在上一篇文章中,我们已经向大家介绍了一个日活跃用户百万级别,处理请求量上亿的电商系统案例。我们选择了这个中型电商系统在大促期间的瞬时高峰下单场景,作为我们的JVM优化分析的场景。通过预测,我们得出在大促高峰期,每台机器每秒需要处理300个订单请求…

蓝桥杯-线性动态规划问题背包问题进阶策略详解-

题目&#xff1a;蓝桥云课-青蛙吃虫 解题代码&#xff1a; #include <iostream> #include<cstring> #include<algorithm> using namespace std;const int N106;int f[N][N]; int a[N]; int t,l,r,k,n;int main() {cin>>t;while(t--){scanf("%d%…

云计算第十二课

安装虚拟机 第一步新建虚拟机 选择自定义安装 下一步 选择稍后安装操作系统 选择系统类型和版本 选择虚拟机文件路径&#xff08;建议每台虚拟机单独存放并且路径不要有中文&#xff09;点击下一步 选择bios下一步 选择虚拟机处理器内核数量 默认硬盘或者自行调大硬盘 选择虚…