共计 3504 个字符,预计需要花费 9 分钟才能阅读完成。
前两天在工作中忙的焦头烂额,波及到 @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
对于事物的管制利用到炉火纯青。哈哈~
举荐一个 Spring Boot 根底实战教程:\
https://github.com/javastacks…
第一点要留神的就是在 @Transactional
注解的办法中,再调用本类中的其余办法 method2 时,那么 method2 办法上的 @Transactional
注解是不!会!生!效!的!然而加上也并不会报错,拿图片简略帮忙了解一下吧。这一点也是面试中会问到的事务生效的场景。
通过代理对象在指标对象前后进行办法加强,也就是事务的开启提交和回滚。那么持续调用本类中其余办法是怎么呢,如下图:
可见指标对象外部的自我调用,也就是通过 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 中实现对事务的管制:实现类(各种状况的阐明都写在图片里了,这样不便浏览,有助于疾速了解吧)
下面两种状况不论使不应用代理调用办法 1 和办法 2,办法 transactionalMethod
都处在一个事务中,四条更新操作全副失败。
那么有人可能会有疑难了,在办法 1 和办法 2 上都加 @Transactional
注解呢?答案是后果和下面是统一的。
小结只有办法 transactionalMethod
上有注解,并且办法 1 和办法 2 都处于以后事务中(不应用代理调用,办法 1 和办法 2 上的 @Transactional
注解是不失效的;应用代理,须要办法 1 和办法 2 都处在 transactionalMethod
办法的事务中,默认或者嵌套事务均可,当然也能够不加 @Transactional
注解),那么整体放弃事务一致性。
如果想要办法 1 和办法 2 均独自放弃事务一致性怎么办呢,刚说过了,如果不是用代理调用 @Transactional
注解是不失效的,所以肯定要应用代理调用实现,而后让办法 1 和办法 2 别离独自开启新的事务,便 OK 啦。上面摆上图片。
这两种状况都是办法 1 和办法 2 均处在独自的事务中,各自放弃事务的一致性。
接下来进行进一步的优化,能够在 transactionalMethod
办法中别离对办法 1 和办法 2 进行管制。要将代码的艺术施展到极致嘛,上面装逼开始。
代码太长了,超过屏幕了,粘贴进去截的图,红框正文须要认真看,心愿不要影响你的浏览体验,至此,本篇对于 @Transactioinal
注解的应用就到此为止啦,
简略总结一下吧:
1、就是 @Transactional
注解保障的是每个办法处在一个事务,如果有 try 肯定在 catch 中抛出运行时异样。
2、办法必须是 public 修饰符。否则注解不会失效,然而加了注解也没啥故障,不会报错,只是没卵用而已。
3、this. 本办法的调用,被调用办法上注解是不失效的,因为无奈再次进行切面加强。
如果有更粗疏的探讨欢送评论,感激浏览。
版权申明:本文为 CSDN 博主「范学博」的原创文章,遵循 CC 4.0 BY-SA 版权协定,转载请附上原文出处链接及本申明。原文链接:https://blog.csdn.net/fanxb92…
近期热文举荐:
1.1,000+ 道 Java 面试题及答案整顿(2022 最新版)
2. 劲爆!Java 协程要来了。。。
3.Spring Boot 2.x 教程,太全了!
4. 别再写满屏的爆爆爆炸类了,试试装璜器模式,这才是优雅的形式!!
5.《Java 开发手册(嵩山版)》最新公布,速速下载!
感觉不错,别忘了顺手点赞 + 转发哦!