热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

一口气说出6种@Transactional注解失效场景

坚持学习,好文每日送达!

点击 “  程序员内点事  ”关注,选择“  设置星标  ”

坚持学习,好文每日送达!

引言

昨天公众号粉丝咨询了一个问题,说自己之前面试被问 @Transactional 注解哪些场景下会失效,一时语塞致使面试失败。所以今天简单的和大家分享一下 @Transactional 相关的知识。

@Transactional  注解相信大家并不陌生,平时开发中很常用的一个注解,它能保证方法内多个数据库操作要么同时成功、要么同时失败。使用 @Transactional 注解时需要注意许多的细节,不然你会发现 @Transactional 总是莫名其妙的就失效了。

一、事务

事务管理在系统开发中是不可缺少的一部分, Spring 提供了很好事务管理机制,主要分为 编程式事务 声明式事务 两种。

编程式事务:是指在代码中手动的管理事务的提交、回滚等操作,代码侵入性比较强,如下示例:

1try {
2 //TODO something
3 transactionManager.commit(status);
4} catch (Exception e) {
5 transactionManager.rollback(status);
6 throw new InvoiceApplyException("异常失败");
7}

声明式事务 :基于 AOP 面向切面的,它将具体业务与事务处理部分解耦,代码侵入性很低,所以在实际开发中声明式事务用的比较多。声明式事务也有两种实现方式,一是基于 TX AOP 的xml配置文件方式,二种就是基于@Transactional注解了。

1    @Transactional
2 @GetMapping("/test")
3 public String test() {
4
5 int insert = cityInfoDictMapper.insert(cityInfoDict);
6 }

二、@Transactional介绍

1、@Transactional注解可以作用于哪些地方?

@Transactional 可以作用在 接口 类方法

  • 作用于类 :当把@Transactional 注解放在类上时,表示所有该类的 public 方法都配置相同的事务属性信息。

  • 作用于方法:当类配置了@Transactional,方法也配置了@Transactional,方法的事务会覆盖类的事务配置信息。

  • 作用于接口:不推荐这种使用方法,因为一旦标注在Interface上并且配置了Spring AOP 使用CGLib动态代理,将会导致@Transactional注解失效

 1@Transactional
2@RestController
3@RequestMapping
4public class MybatisPlusController {
5 @Autowired
6 private CityInfoDictMapper cityInfoDictMapper;
7
8 @Transactional(rollbackFor = Exception.class)
9 @GetMapping("/test")
10 public String test() throws Exception {
11 CityInfoDict cityInfoDict = new CityInfoDict();
12 cityInfoDict.setParentCityId(2);
13 cityInfoDict.setCityName("2");
14 cityInfoDict.setCityLevel("2");
15 cityInfoDict.setCityCode("2");
16 int insert = cityInfoDictMapper.insert(cityInfoDict);
17 return insert + "";
18 }
19}

2、@Transactional注有哪些属性?

propagation属性

propagation  代表事务的传播行为,默认值为  Propagation.REQUIRED ,其他的属性信息如下:

  • Propagation.REQUIRED :如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。 (  也就是说如果A方法和B方法都添加了注解,在默认传播模式下,A方法内部调用B方法,会把两个方法的事务合并为一个事务 

  • Propagation.SUPPORTS :如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务的方式继续运行。

  • Propagation.MANDATORY :如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。

  • Propagation.REQUIRES_NEW :重新创建一个新的事务,如果当前存在事务,暂停当前的事务。 (  当类A中的 a 方法用默认 Propagation.REQUIRED 模式,类B中的 b方法加上采用  Propagation.REQUIRES_NEW 模式,然后在 a 方法中调用 b方法操作数据库,然而 a方法抛出异常后,b方法并没有进行回滚,因为 Propagation.REQUIRES_NEW 会暂停 a方法的事务  )

  • Propagation.NOT_SUPPORTED :以非事务的方式运行,如果当前存在事务,暂停当前的事务。

  • Propagation.NEVER :以非事务的方式运行,如果当前存在事务,则抛出异常。

  • Propagation.NESTED  :和 Propagation.REQUIRED 效果一样。

isolation 属性

isolation  :事务的隔离级别,默认值为  Isolation.DEFAULT

  • Isolation.DEFAULT:使用底层数据库默认的隔离级别。

  • Isolation.READ_UNCOMMITTED

  • Isolation.READ_COMMITTED

  • Isolation.REPEATABLE_READ

  • Isolation.SERIALIZABLE

timeout 属性

timeout  :事务的超时时间,默认值为 -1。如果超过该时间限制但事务还没有完成,则自动回滚事务。

readOnly 属性

readOnly  :指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。

rollbackFor 属性

rollbackFor  :用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。

noRollbackFor 属性**

noRollbackFor :抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。

二、@Transactional失效场景

接下来我们结合具体的代码分析一下哪些场景下,@Transactional 注解会失效。

1、@Transactional 应用在非 public 修饰的方法上

如果 Transactional 注解应用在非 public  修饰的方法上,Transactional将会失效。

一口气说出 6种 @Transactional 注解失效场景
在这里插入图片描述

之所以会失效是因为在Spring AOP 代理时,如上图所示  TransactionInterceptor  (事务拦截器)在目标方法执行前后进行拦截, DynamicAdvisedInterceptor (CglibAopProxy 的内部类)的 intercept 方法或  JdkDynamicAopProxy  的 invoke 方法会间接调用  AbstractFallbackTransactionAttributeSource 的  computeTransactionAttribute  方法,获取Transactional 注解的事务配置信息。

1protected TransactionAttribute computeTransactionAttribute(Method method,
2 Class targetClass) {
3 // Don't allow no-public methods as required.
4 if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
5 return null;
6}

此方法会检查目标方法的修饰符是否为 public,不是 public则不会获取@Transactional 的属性配置信息。

注意: protectedprivate  修饰的方法上使用  @Transactional  注解,虽然事务无效,但不会有任何报错,这是我们很容犯错的一点。

2、@Transactional 注解属性 propagation 设置错误

这种失效是由于配置错误,若是错误的配置以下三种 propagation,事务将不会发生回滚。

TransactionDefinition.PROPAGATION_SUPPORTS

:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

TransactionDefinition.PROPAGATION_NOT_SUPPORTED

:以非事务方式运行,如果当前存在事务,则把当前事务挂起。

TransactionDefinition.PROPAGATION_NEVER :以非事务方式运行,如果当前存在事务,则抛出异常。

3、@Transactional 注解属性 rollbackFor 设置错误

rollbackFor  可以指定能够触发事务回滚的异常类型。Spring默认抛出了未检查 unchecked 异常(继承自  RuntimeException 的异常)或者  Error 才回滚事务;其他异常不会触发回滚事务。如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定  rollbackFor 属性。

一口气说出 6种 @Transactional 注解失效场景
在这里插入图片描述
1// 希望自定义的异常可以进行回滚
2@Transactional(propagation= Propagation.REQUIRED,rollbackFor= MyException.class

若在目标方法中抛出的异常是  rollbackFor  指定的异常的子类,事务同样会回滚。Spring源码如下:

 1private int getDepth(Class exceptionClass, int depth) {
2 if (exceptionClass.getName().contains(this.exceptionName)) {
3 // Found it!
4 return depth;
5}
6 // If we've gone as far as we can go and haven't found it...
7 if (exceptiOnClass== Throwable.class) {
8 return -1;
9}
10return getDepth(exceptionClass.getSuperclass(), depth + 1);
11}

4、同一个类中方法调用,导致@Transactional失效

开发中避免不了会对同一个类里面的方法调用,比如有一个类Test,它的一个方法A,A再调用本类的方法B(不论方法B是用public还是private修饰),但方法A没有声明注解事务,而B方法有。则外部调用方法A之后,方法B的事务是不会起作用的。这也是经常犯错误的一个地方。

那为啥会出现这种情况?其实这还是由于使用 Spring AOP 代理造成的,因为只有当事务方法被当前类以外的代码调用时,才会由 Spring 生成的代理对象来管理。

 1//@Transactional
2 @GetMapping("/test")
3 private Integer A() throws Exception {
4 CityInfoDict cityInfoDict = new CityInfoDict();
5 cityInfoDict.setCityName("2");
6 /**
7 * B 插入字段为 3的数据
8 */

9 this.insertB();
10 /**
11 * A 插入字段为 2的数据
12 */

13 int insert = cityInfoDictMapper.insert(cityInfoDict);
14
15 return insert;
16 }
17
18 @Transactional()
19 public Integer insertB() throws Exception {
20 CityInfoDict cityInfoDict = new CityInfoDict();
21 cityInfoDict.setCityName("3");
22 cityInfoDict.setParentCityId(3);
23
24 return cityInfoDictMapper.insert(cityInfoDict);
25 }

5、异常被你的 catch“吃了”导致@Transactional失效

这种情况是最常见的一种@Transactional注解失效场景

 1    @Transactional
2 private Integer A() throws Exception {
3 int insert = 0;
4 try {
5 CityInfoDict cityInfoDict = new CityInfoDict();
6 cityInfoDict.setCityName("2");
7 cityInfoDict.setParentCityId(2);
8 /**
9 * A 插入字段为 2的数据
10 */

11 insert = cityInfoDictMapper.insert(cityInfoDict);
12 /**
13 * B 插入字段为 3的数据
14 */

15 b.insertB();
16 } catch (Exception e) {
17 e.printStackTrace();
18 }
19 }

如果B方法内部抛了异常,而A方法此时try catch了B方法的异常,那这个事务还能正常回滚吗?

答案:不能!

会抛出异常:

1org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

因为当 ServiceB 中抛出了一个异常以后, ServiceB 标识当前事务需要 rollback 。但是 ServiceA 中由于你手动的捕获这个异常并进行处理, ServiceA 认为当前事务应该正常 commit 。此时就出现了前后不一致,也就是因为这样,抛出了前面的 UnexpectedRollbackException 异常。

spring 的事务是在调用业务方法之前开始的,业务方法执行完毕之后才执行 commit  or  rollback ,事务是否执行取决于是否抛出 runtime异常 。如果抛出 runtime exception  并在你的业务方法中没有catch到的话,事务会回滚。

在业务方法中一般不需要catch异常,如果非要catch一定要抛出 throw new RuntimeException() ,或者注解中指定抛异常类型 @Transactional(rollbackFor=Exception.class) ,否则会导致事务失效,数据commit造成数据不一致,所以有些时候try catch反倒会画蛇添足。

6、数据库引擎不支持事务

这种情况出现的概率并不高,事务能否生效数据库引擎是否支持事务是关键。常用的 MySQL 数据库默认使用支持事务的 innodb 引擎。一旦数据库引擎切换成不支持事务的 myisam ,那事务就从根本上失效了。

总结

@Transactional 注解的看似简单易用,但如果对它的用法一知半解,还是会踩到很多坑的。

今天就说这么多,如果本文对您有一点帮助,希望能得到您一个点赞:+1:哦

您的认可才是我写作的动力!

一口气说出 6种 @Transactional 注解失效场景

往期 精彩 回顾

JAVA 爬虫" tab="innerlink" rel="nofollow,noindex">为了不复制粘贴,我被逼着学会了JAVA爬虫

一口气说出 9种 分布式ID生成方式,面试官有点懵了

面试总被问分库分表怎么办?这些知识点你要懂

基于 Java 实现的人脸识别功能(附源码)

面试被问分布式ID怎么办?滴滴(Tinyid)甩给他

9种分布式ID生成之美团(Leaf)实战

小福利:

通过合法手段,获取到一些极客付费课程 ,嘘~, 免费   送给小伙伴们。关注公众号回复【 极客 】自行领取

一口气说出 6种 @Transactional 注解失效场景

技术/面试/吐槽

程序员内点事这都有

长按扫码可关注

在看点这里

一口气说出 6种 @Transactional 注解失效场景

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 我们


推荐阅读
  • 提升Python编程效率的十点建议
    本文介绍了提升Python编程效率的十点建议,包括不使用分号、选择合适的代码编辑器、遵循Python代码规范等。这些建议可以帮助开发者节省时间,提高编程效率。同时,还提供了相关参考链接供读者深入学习。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了logistic回归(线性和非线性)相关的知识,包括线性logistic回归的代码和数据集的分布情况。希望对你有一定的参考价值。 ... [详细]
  • 2018年人工智能大数据的爆发,学Java还是Python?
    本文介绍了2018年人工智能大数据的爆发以及学习Java和Python的相关知识。在人工智能和大数据时代,Java和Python这两门编程语言都很优秀且火爆。选择学习哪门语言要根据个人兴趣爱好来决定。Python是一门拥有简洁语法的高级编程语言,容易上手。其特色之一是强制使用空白符作为语句缩进,使得新手可以快速上手。目前,Python在人工智能领域有着广泛的应用。如果对Java、Python或大数据感兴趣,欢迎加入qq群458345782。 ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • IB 物理真题解析:比潜热、理想气体的应用
    本文是对2017年IB物理试卷paper 2中一道涉及比潜热、理想气体和功率的大题进行解析。题目涉及液氧蒸发成氧气的过程,讲解了液氧和氧气分子的结构以及蒸发后分子之间的作用力变化。同时,文章也给出了解题技巧,建议根据得分点的数量来合理分配答题时间。最后,文章提供了答案解析,标注了每个得分点的位置。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
author-avatar
johnnyLei
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有