如何将 @Transactional 事务注解运用到炉火纯青?

简介: 前两天在工作中忙的焦头烂额,涉及到@Transactional对于事务的控制,便仔细研究了一下,颇有所获,花费好了几天测试整理,今天才发表出来,希望看到博客的老铁们能有所获吧。话不多说直奔正题。

前两天在工作中忙的焦头烂额,涉及到@Transactional对于事务的控制,便仔细研究了一下,颇有所获,花费好了几天测试整理,今天才发表出来,希望看到博客的老铁们能有所获吧。话不多说直奔正题。

先简单介绍一下Spring事务的传播行为:

所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:

  • TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。
  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。
  • TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
  • TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED

然后说一下Spring事务的回滚机制:

Spring的AOP即声明式事务管理默认是针对unchecked exception回滚。Spring的事务边界是在调用业务方法之前开始的,业务方法执行完毕之后来执行commit or rollback(Spring默认取决于是否抛出runtimeException)。

如果你在方法中有try{}catch(Exception e){}处理,那么try里面的代码块就脱离了事务的管理,若要事务生效需要在catch中throw new RuntimeException ("xxxxxx");这一点也是面试中会问到的事务失效的场景。

再简单介绍一下@Transactional注解底层实现方式吧,毫无疑问,是通过动态代理,那么动态代理又分为JDK自身和CGLIB,这个也不多赘述了,毕竟今天的主题是如何将@Transactional对于事物的控制应用到炉火纯青。哈哈~


第一点要注意的就是在@Transactional注解的方法中,再调用本类中的其他方法method2时,那么method2方法上的@Transactional注解是不!会!生!效!的!但是加上也并不会报错,拿图片简单帮助理解一下吧。这一点也是面试中会问到的事务失效的场景。

微信图片_20220907115444.png

通过代理对象在目标对象前后进行方法增强,也就是事务的开启提交和回滚。那么继续调用本类中其他方法是怎样呢,如下图:

微信图片_20220907115447.png

可见目标对象内部的自我调用,也就是通过this.指向的目标对象将不会执行方法的增强。


先说第二点需要注意的地方,等下说如何解决上面第一点的问题。第二点就是@Transactional注解的方法必须是公共方法,就是必须是public修饰符!!!

至于这个的原因,发表下个人的理解吧,因为JVM的动态代理是基于接口实现的,通过代理类将目标方法进行增强,想一下也是啦,没有权限访问那么你让我怎么进行,,,好吧,这个我也没有深入研究底层,个人理解个人理解。

在这里我也放个问题吧,希望有高手可以回复指点指点我,因为JVM动态代理是基于接口实现的,那么是不是service层都要按照接口和实现类的开发模式,注解才会生效呢,就是说controller层直接调用没有接口的service层,加了注解也一样不起作用吧,这个懒了,没有测试,其一是因为没有人会这么开发吧,其二是我就认为是不起作用的,哈哈

下面来解决一下第一点的问题,如何在方法中调用本类中其他方法呢。

通过AopContext.currentProxy ()获取到本类的代理对象,再去调用就好啦。因为这个是CGLIB实现,所以要开启AOP,当然也很简单,在springboot启动类上加上注解@EnableAspectJAutoProxy(exposeProxy = true)就可以啦,这个依赖大家自行搜一下就好啦。要注意,注意,代理对象调用的方法也要是public修饰符,否则方法中获取不到注入的bean,会报空指针错误。

emmmm,我先把调用的方式和结果说下吧。自己简单写了代码,有点粗糙,就不要介意啦,嘿嘿。。。

Controller中调用Service

@RestController
public class TransactionalController {
    @Autowired
    private TransactionalService transactionalService;
    @PostMapping("transactionalTest")
    public void transacionalTest(){
        transactionalService.transactionalMethod();
    }
}

Service中实现对事务的控制:接口

public interface TransactionalService {
    void transactionalMethod();
}

Service中实现对事务的控制:实现类(各种情况的说明都写在图片里了,这样方便阅读,有助于快速理解吧)

微信图片_20220907115523.png

微信图片_20220907115525.png

上面两种情况不管使不使用代理调用方法1和方法2,方法transactionalMethod都处在一个事务中,四条更新操作全部失败。

那么有人可能会有疑问了,在方法1和方法2上都加@Transactional注解呢?答案是结果和上面是一致的。

小结只要方法transactionalMethod上有注解,并且方法1和方法2都处于当前事务中(不使用代理调用,方法1和方法2上的@Transactional注解是不生效的;使用代理,需要方法1和方法2都处在transactionalMethod方法的事务中,默认或者嵌套事务均可,当然也可以不加@Transactional注解),那么整体保持事务一致性。

如果想要方法1和方法2均单独保持事务一致性怎么办呢,刚说过了,如果不是用代理调用@Transactional注解是不生效的,所以一定要使用代理调用实现,然后让方法1和方法2分别单独开启新的事务,便OK啦。下面摆上图片。

微信图片_20220907115559.png

微信图片_20220907115602.png

这两种情况都是方法1和方法2均处在单独的事务中,各自保持事务的一致性。

接下来进行进一步的优化,可以在transactionalMethod方法中分别对方法1和方法2进行控制。

微信图片_20220907115734.png

代码太长了,超过屏幕了,粘贴出来截的图,红框注释需要仔细看,希望不要影响你的阅读体验,至此,本篇关于@Transactioinal注解的使用就到此为止啦,

简单总结一下吧:

1、就是@Transactional注解保证的是每个方法处在一个事务,如果有try一定在catch中抛出运行时异常。

2、方法必须是public修饰符。否则注解不会生效,但是加了注解也没啥毛病,不会报错,只是没卵用而已。

3、this.本方法的调用,被调用方法上注解是不生效的,因为无法再次进行切面增强。

如果有更细致的讨论欢迎评论,感谢阅读。


相关文章
|
11月前
|
Java API Apache
java集成stable diffusion
通过REST API和JNI两种方法,我们可以在Java应用程序中集成Stable Diffusion模型。REST API方法更加简单和易于维护,而JNI方法则提供更高的性能。根据具体应用场景和需求,选择合适的集成方法,可以充分利用Stable Diffusion的强大功能,实现高效的图像生成和处理。
272 15
|
11月前
|
JSON 缓存 API
深度探索淘宝详情API接口:高效获取商品信息的实践指南
淘宝详情API接口是阿里巴巴开放平台的重要组成部分,帮助开发者通过程序化方式获取淘宝商品的详细信息,如标题、价格、销量等。本文介绍其使用方法,涵盖权限申请、请求构造、异常处理及高级应用,助力开发者优化电商体验和业务决策。
|
11月前
|
数据可视化 搜索推荐 数据挖掘
从理念到实践:电商协作创新的实现路径
在竞争激烈的电商行业,团队亟需解决资源有限下的高效协作与客户满意度提升问题。传统模式存在部门割裂、信息不对称及管理效率低等痛点。创新服务模式通过搭建一体化平台、数据驱动和实时反馈,打破信息壁垒,实现多部门高效协作与智能化服务,借助协作工具如看板,优化任务管理和客户反馈跟踪,最终推动业务增长与品牌长远发展。
|
存储 监控 Linux
docker构建镜像详解!!!
本文回顾了Docker的基本命令和管理技巧,包括容器和镜像的增删改查操作,容器的生命周期管理,以及如何通过端口映射和数据卷实现容器与宿主机之间的网络通信和数据持久化。文章还详细介绍了如何使用Docker部署一个简单的Web应用,并通过数据卷映射实现配置文件和日志的管理。最后,文章总结了如何制作自定义镜像,包括Nginx、Python3和CentOS镜像,以及如何制作私有云盘镜像。
533 2
|
网络协议 网络性能优化 API
TCP或RDMA
【10月更文挑战第1天】TCP或RDMA
672 2
|
JavaScript 数据可视化 定位技术
漏刻有时数据可视化Echarts组件开发(24):本地离线的地图下钻与散点图
漏刻有时数据可视化Echarts组件开发(24):本地离线的地图下钻与散点图
350 0
漏刻有时数据可视化Echarts组件开发(24):本地离线的地图下钻与散点图
|
Linux API 图形学
深度探索Linux操作系统 —— Linux图形原理探讨2
深度探索Linux操作系统 —— Linux图形原理探讨
233 3
|
SQL 人工智能 API
openai停止中国的api服务,但是性能相当的阿里云免费提供迁移
OpenAI暂停中国API服务,阿里云百炼响应迅速,提供免费tokens(2200万)与迁移服务给受影响开发者。Qwen2-72B与GPT-4同列全球第四(HELM MMLU榜)。Qwen-plus调用成本仅GPT-4的1/50。阿里云百炼以开放性著称,兼容LlamaIndex等,支持多种数据源及自定义组件,加速AI应用集成。官网有丰富资源,助力快速上手大模型开发。
588 0
|
敏捷开发 持续交付 开发工具
阿里云云效产品使用问题之如何把单独的库移到组里面
云效作为一款全面覆盖研发全生命周期管理的云端效能平台,致力于帮助企业实现高效协同、敏捷研发和持续交付。本合集收集整理了用户在使用云效过程中遇到的常见问题,问题涉及项目创建与管理、需求规划与迭代、代码托管与版本控制、自动化测试、持续集成与发布等方面。
|
Dubbo Java 应用服务中间件
带你读《Apache Dubbo微服务开发从入门到精通》——三、 Annotation配置(下)
带你读《Apache Dubbo微服务开发从入门到精通》——三、 Annotation配置(下)
547 92