声明式事务还是编程式事务,如何选择?

(/≧▽≦)/~┴┴ 嗨~我叫小奥 ✨✨✨
👀👀👀 个人博客:小奥的博客
👍👍👍:个人CSDN
⭐️⭐️⭐️:Github传送门
🍹 本人24应届生一枚,技术和水平有限,如果文章中有不正确的内容,欢迎多多指正!
📜 欢迎点赞收藏关注哟! ❤️

文章目录

  • Spring之如何选择事务
    • 1. 编程式事务
    • 2. 声明式事务
      • (1)优点
      • (2)问题
    • 3. 如何选择

Spring之如何选择事务

因为博主之前的实习经历中有使用编程式事务的经历,因为当时是同事给的方案,所以并没有深究为什么不用编程式事务,最近刚好在面试,有面试官问到:为什么不使用声明式事务?因为现在基本很多场景下我们都是直接使用@Transactional注解直接开启事务,所以很少会去使用编程式事务。

所以今天我们就借助这个机会好好来总结一下应该如何选择。

Spring官方文档中对事务的描述如下:

翻译文档链接:传送门

Spring解决了全局和局部事务的弊端。它让应用开发者在任何环境下都能使用一个一致的编程模型。你只需写一次代码,它就可以在不同的环境中受益于不同的事务管理策略。Spring框架同时提供声明式编程式事务管理。大多数用户更喜欢声明式事务管理,我们在大多数情况下都推荐这样做

通过编程式事务管理,开发人员使用Spring框架的事务抽象,它可以在任何底层事务基础设施上运行。通过首选的声明式模型,开发人员通常很少或不写与事务管理有关的代码,因此,不依赖于Spring框架的事务API或任何其他事务API。

下面我们就带大家来学习一下什么是声明式事务和编程式事务。

1. 编程式事务

Spring框架提供了两种编程式事务管理的手段,通过使用:

  • TransactionTemplateTransactionalOperator.
  • 直接实现一个 TransactionManager

Spring团队通常推荐 TransactionTemplate 用于强制性流程中的编程式事务管理, TransactionalOperator 用于响应式代码。第二种方法类似于使用JTA的 UserTransaction API,尽管异常处理没有那么麻烦。

代码示例如下:

public class SimpleService implements Service {// single TransactionTemplate shared amongst all methods in this instanceprivate final TransactionTemplate transactionTemplate;// use constructor-injection to supply the PlatformTransactionManagerpublic SimpleService(PlatformTransactionManager transactionManager) {this.transactionTemplate = new TransactionTemplate(transactionManager);}public Object someServiceMethod() {return transactionTemplate.execute(new TransactionCallback() {// the code in this method runs in a transactional contextpublic Object doInTransaction(TransactionStatus status) {updateOperation1();return resultOfUpdateOperation2();}});}
}

可以看到,编程式事务在代码中硬编码(不推荐使用) : 通过 TransactionTemplate或者 TransactionManager 手动管理事务,实际应用中很少使用,但是对于你理解 Spring 事务管理原理有帮助。

2. 声明式事务

Spring框架的声明式事务管理是通过Spring面向切面编程(AOP)实现的。然而,由于事务方面的代码是随Spring框架的发布而来,并且可以以模板的方式使用,所以一般不需要理解AOP的概念来有效地使用这些代码。

@Transactional 注解来注解你的类,将 @EnableTransactionManagement 添加到你的配置中。基于注解的方法。直接在Java源代码中声明事务语义,使声明更接近于受影响的代码。没有太多的过度耦合的危险,因为无论如何,要以事务方式使用的代码几乎总是以这种方式部署。

代码示例:

// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {@Overridepublic Foo getFoo(String fooName) {// ...}@Overridepublic Foo getFoo(String fooName, String barName) {// ...}@Overridepublic void insertFoo(Foo foo) {// ...}@Overridepublic void updateFoo(Foo foo) {// ...}
}

(1)优点

  • 代码简洁,使用方便:从上面的示例代码中可以看到,声明式事务帮我们节省了很多的编码,会自动帮助我们进行事务的开启、提交以及回滚等操作;
  • 对代码没有侵入性:声明式事务管理是基于AOP的方式来实现的,本质就是在目标方法执行前后进行拦截,在目标方法执行前加入或者创建一个事务,在执行方法后,根据实际情况选择提交或者回滚事务。

(2)问题

  • 颗粒度问题:声明式事务有一个局限,就是最小粒度要作用在方法上。也就是说,如果想要给一部分代码添加事务,那就必须这部分代码独立拉出来作为一个方法。
  • 配置问题:因为声明式事务是通过注解的,有些时候还可以通过配置实现,这就会导致一个问题,那就是这个事务有可能被开发者忽略

首先,如果开发者没有注意到一个方法是被事务嵌套的,那么就可能会再方法中加入一些如RPC远程调用、消息发送、缓存更新、文件写入等操作。

我们知道,这些操作如果被包在事务中,有两个问题:

1、这些操作自身是无法回滚的,这就会导致数据的不一致。可能RPC调用成功了,但是本地事务回滚了,可是PRC调用无法回滚了。

2、在事务中有远程调用,就会拉长整个事务。那么久会导致本事务的数据库连接一直被占用,那么如果类似操作过多,就会导致数据库连接池耗尽。

  • 事务失效:声明式事务虽然看上去帮我们简化了很多代码,但是有一些场景会使得声明式事务失效,这也是一个比较常见的八股文了,如下:

Spring框架提供了非常方便的事务管理机制,但是在某些情况下,Spring事务会失效。

(1)方法非public。如果一个方法不是public修饰的,那么Spring无法代理这个方法,Spring官方文档中有明确的说明,@Transactional只有在修饰public方法时才会生效。修改pretected、private或者保内可见的方法均不会生效。

(2)事务方法被final、static关键字修饰。事务方法被final修改会防止子类重写该方法,从而无法进行动态代理,事务方法被static修改会使得该方法属于类而不是对象,因此也无法进行动态代理。

(3)方法自调用。当一个类中的一个方法调用同一个类中的另一个@Transactional方法时,这种情况被称为方法自调用,在这种情况下,事务控制将无法正常工作。因为事务代理对象在外部调用时才会被创建,如果一个方法在同一个类中调用另一个方法,实际上是在当前实例中进行调用,而不是通过代理对象调用。

(4)异常被捕获。Spring默认只对未被捕获的异常进行回滚,如果异常被捕获并处理了,那么Spring无法感知到异常的存在,也就无法进行回滚,因此,如果需要事务回滚,必须确保异常未被捕获并没有被处理。

(5)没有被Spring管理。只有在Spring容器中管理的Bean上的@Transactional注解才会生效,如把@Service注解注释掉,这个类就不会被加载成为一个Bean,在该对象的方法上添加@Transactional注解将不会有任何效果。

(6)数据库不支持事务。Spring事务是通过底层的数据库事务实现的。如果底层数据库不支持事务,那么Spring自然无法实现事务管理,如果需要使用事务,选择支持事务的数据库引擎,如MySQL的InnoDB引擎。

3. 如何选择

关于如何选择事务的使用方式,我们可以从@Transactional失效的场景来考虑,如果考虑到后续可能无法避免事务失效的问题,那么我建议还是最好使用编程式事务,因为硬编码其实简单粗暴,不管三七二十一,强制让事务执行。

其实关于@Transactional的用法,Java手册中也有对应的规范如下:

在这里插入图片描述

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

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

相关文章

nut-ui组件库icon中使用阿里图标

1.需求 基本每个移动端组件库都有组件 icon组件 图标组件、 但是很多组件库中并找不到我们需要的图标 这时候 大家有可能会找图标库 最大众的就是iconfont的图标了 2.使用 有很多方式去使用这个东西 比如将再限链接中的css引入 在使用 直接下载图标 symbol 方式 等....…

uniapp 开发出现这个 Error in onLoad hook: “SyntaxError: Unexpected end of JSON input“

uniapp 开发出现这个 Error in onLoad hook: “SyntaxError: Unexpected end of JSON input“,这个问题如何解决。 原因: 由于JSON.parse无法识别某些url中的特殊字符比如&等特殊符号造成的。 解决办法: 页面A(JSON.stringify传参&a…

‍❄️Unity 官方免费资源大放送!森林、沙漠、北极等 350+逼真的自然纹理 Megapack等你来拿

🌳350自然纹理 Megapack,免费获取!🎁 你是否热爱游戏开发,却被昂贵的资源库所困扰?你是否渴望创造出令人惊叹的自然场景,却被有限的纹理素材所束缚?如果你的答案是肯定的&#xff0c…

Kotlin编程权威指南学习知识点预览

一、变量、常量和类型: 变量、常量以及 Kotlin 基本数据类型。变量和常量在 应用程序中可用来储值和传递数据。类型则用来描述常量或变量中保存的是什么样的数据。 1、声明变量: // 变量定义关键字 —— 变量名 —— 类型定义 —— 赋值运算符 —— 赋值var na…

MySQL中ON DUPLICATE KEY UPDATE的介绍与使用、批量更新、存在即更新不存在则插入

文章目录 一、ON DUPLICATE KEY UPDATE的介绍二、ON DUPLICATE KEY UPDATE的使用2.1、案例一:根据主键id进行更新2.2、案例二:根据唯一索引进行更新(常用)2.3、案例三:没有主键或唯一键字段值相同就插入2.4、案例四&am…

前端和后端权限控制【笔记】

前端权限设置【笔记】 前言版权推荐前端权限设置需求效果实现资源 后端权限控制1.给所有前端请求都携带token2.添加拦截器3.配置到WebMvcConfiguration4.更多的权限验证 最后 前言 2024-3-15 18:27:26 以下内容源自《【笔记】》 仅供学习交流使用 版权 禁止其他平台发布时删…

点的基本操作

点的基本操作 要求 提供空间点数据文本文件,包含ID、name、X、Y四个字段信息, 1)读取数据,并且在窗口中显示点的具体位置,用实心圆绘制。 2)鼠标任意点击三个点,将点连线,用黑色笔…

误分区酿苦果,数据恢复有妙方

一、误操作引发分区混乱 在数字化时代的浪潮中,硬盘分区成为我们管理和存储数据的重要手段。然而,误分区这一操作失误,却时常给许多用户带来不小的困扰。误分区,简单来说,就是在对硬盘进行分区操作时,由于…

[云原生] Prometheus之部署 Alertmanager 发送告警

一、Alertmanager 发送告警的介绍 Prometheus 对指标的收集、存储与告警能力分属于 Prometheus Server 和 AlertManager 两个独立的组件,前者仅负责定义告警规则生成告警通知, 具体的告警操作则由后者完成。 Alertmanager 负责处理由 Prometheus Serve…

807补充(十一)(鞍论与随机逼近理论篇)

807补充(十一)(鞍论与随机逼近理论篇) 一.高等概率论初步 Theorem: σ − \sigma- σ− 代数,如果样本空间 Ω \Omega Ω 的一系列子集的集合 F \mathcal{F} F 满足: (1) ∅ ∈ F \emptyset \in \mathcal{F} ∅∈F (2) 若 A …

AVCE - AV Evasion Craft Online 更新 8 种加载方式 - 过 WD 等

免责声明:本工具仅供安全研究和教学目的使用,用户须自行承担因使用该工具而引起的一切法律及相关责任。作者概不对任何法律责任承担责任,且保留随时中止、修改或终止本工具的权利。使用者应当遵循当地法律法规,并理解并同意本声明…

LeetCode 热题 100 | 回溯(二)

目录 1 39. 组合总和 2 22. 括号生成 3 79. 单词搜索 菜鸟做题,语言是 C,感冒快好版 关于对回溯算法的理解请参照我的上一篇博客; 在之后的博客中,我将只分析回溯算法中的 for 循环。 1 39. 组合总和 题眼:c…