Spring @Transactional 注解

官方文档:https://docs.spring.io/spring-framework/reference/data-access/transaction/declarative/annotations.html#:~:text=The%20%40Transactional%20annotation%20is%20metadata,suspending%20any%20existing%20transaction%22).

推荐阅读:https://www.marcobehler.com/guides/spring-transaction-management-transactional-in-depth

Spring 事务的原理

@Transactional 注解的底层实现使用的是 JDBC 事务实现的,所以如果需要使用 @Transactional 的话那么就需要使用支持的数据库,比如 MySQL 的 innodb 就至此事务,下面是简化代码:

import java.sql.Connection;Connection connection = dataSource.getConnection(); // (1)try (connection) {connection.setAutoCommit(false); // (2)// execute some SQL statements...connection.commit(); // (3)} catch (SQLException e) {connection.rollback(); // (4)
}

(1)你需要连接到数据库才能启动事务。DriverManager.getConnection(url, user, password) 也可以,但在大多数企业应用程序中,你会配置数据源并从中获取连接。这是在 Java 中“启动”数据库事务的唯一方式,尽管名称听起来有点奇怪。

(2)setAutoCommit(true) 确保每个单独的 SQL 语句都自动包装在自己的事务中,而 setAutoCommit(false) 则相反:你是事务的管理者,需要开始调用 commit 和其他相关方法。请注意,autoCommit 标志在连接打开的整个时间段内有效,这意味着你只需调用该方法一次,而不是重复调用。

(3)让我们提交我们的事务…

(4)或者,如果出现异常,回滚我们的更改。

这段代码虽然简单,但是确实 Spring 的 @Transactional 帮我们实现事务的核心代码

同时对应的 @Transactional 对应的注解是相关的参数都有 JDBC 的阐述

比如

@Transactional(propagation=TransactionDefinition.NESTED,isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTED)

对应的就是 JDBC 的 savePoint 和 isolate

import java.sql.Connection;// isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTEDconnection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED); // (1)// propagation=TransactionDefinition.NESTEDSavepoint savePoint = connection.setSavepoint(); // (2)
...
connection.rollback(savePoint);
  1. Spring @Transactional 对应的 isolation 也是设置的 JDBC 实现的
  2. 嵌入式事务的实现是通过设置 savePoint

Spring 的事务管理如何工作

我们需要明白一点就是 Spring 的 @Transactional 不是洪水猛兽,我们不需要太过于害怕。反正就是 JDBC 事务的 start 、commit 、rollback 就是这些操作。

但是问题出在,使用普通 JDBC,只有一种方法 (setAutocommit(false)) 来管理事务,而 Spring 为您提供了许多不同的、更方便的方法来实现相同的目的。

如何实现的?我们后面会慢慢来说!

如何使用 Spring 编程式事务 ?

@Service
public class UserService {@Autowiredprivate TransactionTemplate template;public Long registerUser(User user) {Long id = template.execute(status ->  {// execute some SQL that e.g.// inserts the user into the db and returns the autogenerated idreturn id;});}
}

和普通 JDBC 事务进行对比:

  1. 不需要手动创建、关闭资源
  2. 不需要捕获 SQLExceptions ,Spring 会把 SQLExceptions 转换成 RuntimExceptions
  3. 更符合 Spring 生态,使用 @Autowired

使用编程式事务有一个好处就是,我们可以控制事务的最小粒度增加执行的效率!

使用 xml 配置 Spring 事务

之前这种配置很常见,但是现在注解式编程更加常见‘

<!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) --><tx:advice id="txAdvice" transaction-manager="txManager"><!-- the transactional semantics... --><tx:attributes><!-- all methods starting with 'get' are read-only --><tx:method name="get*" read-only="true"/><!-- other methods use the default transaction settings (see below) --><tx:method name="*"/></tx:attributes></tx:advice>

还需要指定对应的 AOP 切面

<aop:config><aop:pointcut id="userServiceOperation" expression="execution(* x.y.service.UserService.*(..))"/><aop:advisor advice-ref="txAdvice" pointcut-ref="userServiceOperation"/>
</aop:config><bean id="userService" class="x.y.service.UserService"/>

service 代码

public class UserService {public Long registerUser(User user) {// execute some SQL that e.g.// inserts the user into the db and retrieves the autogenerated idreturn id;}
}

使用注解配置 Spring 事务

@Service
public class UserService {@Transactionalpublic Long registerUser(User user) {// execute some SQL that e.g.// inserts the user into the db and retrieves the autogenerated id// userDao.save(user);return id;}
}

没有一点 xml 的配置,但是我们需要注意两点:

  1. 需要确保 Spring 配置类需要 @EnableTransactionManagement 注解(SpringBoot 会自动帮我们配置)

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication {.....
    }
    
  2. 确保您在 Spring 配置中指定了事务管理器(无论如何都需要这样做)

  3. 然后 Spring 非常方便,用 @Transactional 注释标识的任何 bean 的公共方法都将在数据库事务内执行

所以如果需要 @Transactional 生效的代码:

@Configuration
@EnableTransactionManagement
public class MySpringConfig {@Beanpublic PlatformTransactionManager txManager() {return yourTxManager; // more on that later}}

我们根据上文提到的 JDBC 方面的知识,我们可以猜到底层的实现:

public class UserService {public Long registerUser(User user) {Connection connection = dataSource.getConnection(); // (1)try (connection) {connection.setAutoCommit(false); // (1)// execute some SQL that e.g.// inserts the user into the db and retrieves the autogenerated id// userDao.save(user); <(2)connection.commit(); // (1)} catch (SQLException e) {connection.rollback(); // (1)}}
}

CGlib & JDK Proxies

还是动态代理实现的 @Transactional 的底层,Spring 帮我们在底层实现了

当一个 bean 上使用 @Transactional 时,Spring 都会使用到动态代理。它不仅实例化 UserService,而且还实例化该 UserService 的事务代理。它通过使用 Cglib 库中的 proxy-through-subclassing 方法来实现

image-20240318093220184

从上面的图片可以了解到 动态代理 帮我们做的工作

  • 打开和关闭数据库连接/事务。
  • 然后调用真正的 UserService
  • 其他的 bean 比如 UserRestController “永远” 不会知道 TA 调用的 UseService 不是真的而是动态代理的对象

为什么需要事务管理器(如 PlatformTransactionManager)?

UserService 会动态代理,而代理会为您管理事务。但实际上并非代理本身处理所有的事务状态(打开、提交、关闭),而是代理将这些工作委托给了事务管理器

Spring 提供 PlatformTransactionManager / TransactionManager 两个接口并且提供了实现

它完全做的就是你迄今为止管理事务所做的一切,但首先,让我们看一下所需的 Spring 配置:

@Bean
public DataSource dataSource() {return new MysqlDataSource(); // (1)
}@Bean
public PlatformTransactionManager txManager() {return new DataSourceTransactionManager(dataSource()); // (2)
}
  1. 创建一个特定于数据库或连接池的数据源。本示例中使用MySQL
  2. 创建你的事务管理器,它需要一个数据源来管理事务

下面就是一个简化版本的 transaction class doBegin 方法作用是开启事务, doCommit 方法就是提交事务

public class DataSourceTransactionManager implements PlatformTransactionManager {@Overrideprotected void doBegin(Object transaction, TransactionDefinition definition) {Connection newCon = obtainDataSource().getConnection();// ...con.setAutoCommit(false);// 是的就这些,没其他的了!}@Overrideprotected void doCommit(DefaultTransactionStatus status) {// ...Connection connection = status.getTransaction().getConnectionHolder().getConnection();try {con.commit();} catch (SQLException ex) {throw new TransactionSystemException("Could not commit JDBC transaction", ex);}}
}

image-20240318094417687

  1. 如果 Spring 检测到 bean 上的 @Transactional 注释,它会创建该 bean 的动态代理。
  2. 代理可以访问事务管理器,并要求它打开和关闭事务/连接。
  3. 事务管理器本身将做在普通 Java 部分中所做的操作,creat、rollback、commit …

物理事务和逻辑事务有什么区别?

@Service
public class UserService {@Autowiredprivate InvoiceService invoiceService;@Transactionalpublic void invoice() {invoiceService.createPdf();// send invoice as email, etc.}
}@Service
public class InvoiceService {@Transactionalpublic void createPdf() {// ...}
}

UserService 有一个事务性 Invoice() 方法。它调用 InvoiceService 上的另一个事务方法 createPdf()。

现在数据库只有一个数据库事务. (就是 getConnection(). setAutocommit(false). commit())Spring 称这个叫做物理事务

但是在对于 Spring 来说有发生了两个逻辑事务:第一个在 UserService 中,另一个在 InvoiceService 中。 Spring 非常智能,知道这两个 @Transactional 方法应该使用相同的物理事务之中

但是当我做出如下的修改:

@Service
public class InvoiceService {@Transactional(propagation = Propagation.REQUIRES_NEW)public void createPdf() {// ...}
}

那么物理事务就是两个了( getConnection() x2. setAutocommit(false) x2. commit() x2),同时逻辑事务也是两个

@Transactional 传播级别

首先介绍一下 @Transactional 的传播级别

  @Transactional(propagation = Propagation.REQUIRED)// or@Transactional(propagation = Propagation.REQUIRES_NEW)// etc

一共有一下几个传播级别:

  • REQUIRED
  • SUPPORTS
  • MANDATORY
  • REQUIRES_NEW
  • NOT_SUPPORTED
  • NEVER
  • NESTED

我们知道 JDBC 的事务能够做到的事情,但是 Spring 到底是如何通过 JDBC 的事务实现的这么多的级别?

  • REQUIRED(默认)方法需要事务,但是如果存在一个事务就使用存在的事务 → getConnection(). setAutoCommit(false). commit
  • SUPPORTS 无论有没有事务都会执行 → JDBC 什么也不会做
  • MANDATORY 如果没有事务就会抛出异常,有事务正常执行 → JDBC 什么也不会做
  • REQUIRES_NEW 我想要一个完全的属于我自己的事务 → getConnection(). setAutocommit(false). commit().
  • NOT_SUPPORTED 以非事务方式执行,如果存在当前事务,则暂停当前事务 → JDBC 什么也不做
  • NEVER 以非事务执行,如果有事务就报错 → JDBC 什么也不做
  • NESTED 如果有事务那么就用嵌入式的事务执行,好像有些复杂,但是其实这个是关于 savePoint → connection.setSavepoint()

@Transactional 隔离级别

当我们进行下面配置

@Transactional(isolation = Isolation.REPEATABLE_READ)

底层其实是这样的:

connection.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);

还要注意,在事务过程中切换隔离级别时,必须确保 JDBC 驱动程序/数据库,了解支持的情况和不支持的情况

最常见的 @Transactional 坑

@Service
public class UserService {@Transactionalpublic void invoice() {createPdf();// send invoice as email, etc.}@Transactional(propagation = Propagation.REQUIRES_NEW)public void createPdf() {// ...}
}

这个 UserService 里面有两个 @Transactional 标识的方法,而且其中的 creatPdf 方法的传播级别是 REQUIRES_NEW

那么现在有一个问题,这个类之中有几个物理事务

答案是 1 个事务而不是 2 个物理事务!!!

为什么?

Spring 为创建事务性 UserService 代理,但是一旦您进入 UserService 类并调用其他内部方法,就不再涉及代理了!这意味着不会再有新的事务

image-20240318115454760

但是这个问题也是有对应的解法

1、可以自己注入自己然后调用 @Lazy @Resource private UserService userService;
2、可以使用 ((UserService) AopContext.currentProxy()).creatPdf()
3、使用 SpringUtil 获取 bean 调用 creatPdf() 方法

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

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

相关文章

基于springboot实现大创管理系统【项目源码+论文说明】计算机毕业设计

基于springboot实现大创管理系统演示 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了大创管理系统的开发全过程。通过分析大创管理系统管理的不足&#xff0c;创建了一个计算机管理大创管理系统的方案。文章介…

Re65:读论文 GPT-3 Language Models are Few-Shot Learners

诸神缄默不语-个人CSDN博文目录 诸神缄默不语的论文阅读笔记和分类 论文全名&#xff1a;Language Models are Few-Shot Learners ArXiv网址&#xff1a;https://arxiv.org/abs/2005.14165 2020 NeurIPS&#xff1a;https://papers.nips.cc/paper/2020/hash/1457c0d6bfcb49674…

集成sa-token实现登录和RBAC权限控制

集成sa-token实现登录和RBAC权限控制 文章目录 1.sa-token是什么&#xff1f;1.1简介1.2官网1.3 Sa-Token 功能一览1.4 功能结构图 2.集成sa-token及配置2.1 pom依赖2.2 yaml配置2.3 代码配置 4.RBAC权限控制表设计5.菜单权限树构造实现5.1菜单权限数据sql查询5.2菜单权限树构建…

solidity(10)

&#x1f52e; Solidity变量指南&#xff1a;探寻状态、局部和全局三界之秘&#x1f30c; 在Solidity的神秘大陆上&#xff0c;变量就像是散落在时间和空间中的宝石&#x1f48e;&#xff0c;每颗都有它独特的魔力和作用域。掌握这三种变量&#xff0c;是成为合约法师的重要一…

Vue项目学习(一)-SQL闯关

Hello , 我是小恒不会java。今天来阅读一个Vue纯前端项目--SQL在线闯关 进步的方法除了文档书籍视频&#xff0c;学会阅读源代码&#xff0c;从代码中学会解决需求的方法也是必要的 已部署完成&#xff0c;在线体验&#xff1a;http://sql.yunduanjianzhan.cn 背景 简介 闯…

《黑神话:悟空》现已正式上架PS商城,今晚或有大动作

关于《黑神话&#xff1a;悟空》的消息可谓是喜闻乐见&#xff01;今天晚上19:10可能会有相关游戏内容放出&#xff0c;让人非常期待。而海信电视推出的《黑神话&#xff1a;悟空》专属画质模式&#xff0c;让玩家可以享受到更加细腻的游戏画面。 此外&#xff0c;海信和《黑神…

高级感拉满的个人UI网页

效果图 PC端 移动端 部分代码 index.html <!DOCTYPE html> <html><head><meta charset"utf-8" /><title>Zboy的主页</title><link rel"stylesheet" href"css/normalize.css" /><link rel&qu…

SAP项目任务一览表

根据SAP Activate项目管理方法论的主要精神&#xff0c;浓缩到一些主要的团队和任务。 主要的团队有&#xff1a; 项目管理(办公室)Project Management(office)&#xff1a;项目经理团队&#xff0c;包括项目办公室。负责项目整体运行和监控&#xff0c;项目办公室负责项目的…

李沐-19 卷积层【动手学深度学习v2】

记录下关于权重下标变换的理解&#xff1a; 从原来的Wi,j到Wi,j,k,l是从二维到四维的过程&#xff0c;如下图所示 对全连接层使用平移不变性(如&#xff1a;卷积核在移动过程是不变的)和局部性&#xff08;如&#xff1a;卷积核有一定大小&#xff09;得到卷积层&#xff0c;这…

【leetcode】双指针算法技巧——滑动窗口

标题&#xff1a;【leetcode】双指针算法技巧——滑动窗口 水墨不写bug 正文开始&#xff1a; 滑动窗口介绍 滑动窗口是一种常用的算法技巧&#xff0c;用于解决一些涉及 连续子数组或子串 的问题。它的基本思想是 维护一个窗口&#xff0c;通过 在窗口内移动 来寻找满…

如何查询RGB图像的三维numpy数组中有多少个不同的RGB点,并打印具体数值?

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

xgplayer插件的使用-西瓜播放器 ---- Vue3中使用

xgplayer 视频 一、xgplayer官网 xgplayer官网 - 点我进入 一、xgplayer简单介绍 西瓜播放器是字节跳动推出的一款播放器。 稳定性好&#xff08;大厂&#xff08;字节跳动&#xff09;出品&#xff09;&#xff1b;插件方便实用且简洁高雅&#xff1b;官网文档清晰&#xff1b…

[Linux - C] 自主Shell

[Linux - C] 自主Shell [Linux - C语言] 自主Shell逻辑策划 main()打印命令行 void MakeCommandLineAndPrint()用户名 USER主机名 HOSTNAME当前目录 PWDSkipPath 切割目录打印命令行 获取用户字符串 int GetUserCommand()检查重定向 void CheckRedir()切割字符 void SplitComma…

数据加密、文档加密为什么都选择安企神软件

数据加密、文档加密为什么都选择安企神软件 免费试用安企神 在数据加密和文件加密领域&#xff0c;有众多优秀的软件&#xff0c;他们功能各异、价格不同、效果也大相径庭&#xff0c;经过对比使用、用户口碑和技术网站评判&#xff0c;安企神在各方面都稳坐第一把交易。其原…

新闻媒体行业邮件推广:精准推送,创造价值

在当今信息爆炸的时代&#xff0c;新闻行业如何在竞争激烈的市场中脱颖而出&#xff0c;吸引读者的目光&#xff0c;成为了每个新闻机构都需要认真思考的问题。许可式邮件营销成为了一种强大的工具&#xff0c;不仅能够向订阅者发送新闻期刊&#xff0c;还能够向广告商发送宣传…

【基础物理实验】【AFM虚拟实验】基于AFM的物质表面微观结构及力学性质表征仿真实验(下)【北京航空航天大学】

本次实验&#xff08;上&#xff09;见博客&#xff1a;【基础物理实验】【AFM虚拟实验】基于AFM的物质表面微观结构及力学性质表征仿真实验&#xff08;上&#xff09;【北京航空航天大学】 本次实验&#xff08;中&#xff09;见博客&#xff1a;【基础物理实验】【AFM虚拟实…

LLamaSharp加载llama.cpp转化好的模型

新建.net8控制台项目 安装依赖包 LLamaSharp和LLamaSharp.Backend.Cpu 准备好转化好的模型 没有的话参考这篇文章https://blog.csdn.net/qq_36437991/article/details/137248622 编写代码 using LLama; using LLama.Common; using LLama.Native;namespace llamasharpstu…

N皇后问题(DFS解决)

文章目录 一、题目分析二、对角线判断&#xff08;分两种&#xff09;三、代码演示 先赞后看&#xff0c;养成习惯&#xff01;&#xff01;&#xff01;^ _ ^<3 ❤️ ❤️ ❤️ 码字不易&#xff0c;大家的支持就是我坚持下去的动力。点赞后不要忘了关注我哦&#xff01; 一…

全球7大指纹浏览器排行榜:哪个最适合你?

在数字时代&#xff0c;我们每一次上网都会留下独特的数字足迹&#xff0c;被称为“浏览器指纹”。为了保护这些私人信息不被滥用&#xff0c;指纹浏览器成为了一个重要工具。但是&#xff0c;并非所有的指纹浏览器都是一样的&#xff0c;它们各有特点&#xff0c;适用于不同的…

数字乡村创新实践探索农业现代化路径:科技赋能农业产业升级、提升乡村治理效能与农民幸福感

随着信息技术的快速发展和数字化时代的到来&#xff0c;数字乡村建设正成为推动农业现代化、提升农业产业竞争力、优化乡村治理以及提高农民幸福感的重要途径。本文将围绕数字乡村创新实践&#xff0c;探讨其在农业现代化路径中的积极作用&#xff0c;以及如何通过科技赋能实现…