当前位置: 首页 > 后端技术 > Java

公司新同事@Transactional事务注解使用得非常熟练,.

时间:2023-04-01 22:26:55 Java

前两天工作很忙,涉及到@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,即声明式事务管理,默认是对uncheckedexception进行回滚。Spring的事务边界在调用业务方法之前启动,在业务方法执行之后执行commit或rollback(Spring的默认取决于是否抛出runtimeException)。如果方法中有try{}catch(Exceptione){}处理,那么try中的代码块就脱离了事务的管理。要使事务生效,需要抛出newRuntimeException("xxxxxx");在catch中也是面试中会问到的交易失败的场景。下面简单介绍一下@Transactional注解的底层实现。毫无疑问是通过动态代理,然后动态代理分为JDK本身和CGLIB。控制被应用到完美。哈哈~推荐一个SpringBoot基础实战教程:\https://github.com/javastacks...首先要注意的是,在@Transactional注解的方法中调用本类中的其他方法method2时,那么@Transactionalmethod2方法上的注释是no!会议!出生!影响!的!但是加上不会报错,只是拍个图帮助大家理解。这也是面试中会问到的交易失败的场景。通过代理对象在目标对象前后进行方法增强,即事务的开启、提交和回滚。那么继续调用这个类中的其他方法呢,如下图所示:可以看出,目标对象内部的自调用,也就是this指向的目标对象。不会执行方法增强。先说第二点需要注意的,再说说如何解决上面第一点的问题。第二点,@Transactional注解方法必须是public方法,即必须是public修饰符!!!至于为什么会这样,说说我个人的理解吧,因为JVM的动态代理是基于接口实现的,通过代理类来增强target方法。想想看。如果你没有权限访问,那你让我怎么做,,,好吧,这个我没有深究底层,个人理解。这里也提一个问题,希望高手指点,因为JVM动态代理是基于接口实现的,所以服务层必须遵循接口和实现类的开发方式,这样注释将生效。也就是说,controller层直接调用service层,没有接口,加了注解也不行。这是偷懒,没有测试。一是因为没人会这么开发,二是我觉得行不通。是的,哈哈,我们来解决第一个问题,如何在方法中调用这个类中的其他方法。通过AopContext.currentProxy()获取这个类的代理对象,然后调用它。因为这是一个CGLIB实现,所以开启AOP当然是非常简单的。只需在springboot启动类中添加注解@EnableAspectJAutoProxy(exposeProxy=true)即可。这就要靠大家自己去寻找了。需要注意的是,代理对象调用的方法也必须有public修饰符,否则方法中无法获取注入的bean,会报空指针错误。emmmmm,先说一下调用的方法和结果吧。代码是我自己简单写的,有点粗糙,大家不要介意,呵呵。..在控制器中调用服务@RestControllerpublicclassTransactionalController{@AutowiredprivateTransactionalServicetransactionalService;@PostMapping("transactionalTest")publicvoidtransacionalTest(){transactionalService.transactionalMethod();}}Service实现事务控制:interfacepublicinterfaceTransactionalService{voidtransactionalMethod();}Service实现事务控制:实现类(图中写了各种情况的描述,便于阅读,有助于快速理解)以上两种情况不使用代理调用方法一和方法二,方法transactionalMethod在一个事务中,四个更新操作都失败。那么可能有人会有疑问,方法一和方法二都加上@Transactional注解怎么样?答案是和上面的结果是一致的。总结只要方法transactionalMethod上有注解,方法1和方法2都在当前事务中(不调用代理,方法1和方法2上的@Transactional注解都不会生效;使用代理需要方法1都在而方法2在transactionalMethod方法的事务中,默认或者嵌套事务是可以接受的,当然你也可以不加@Transactional注解),这样就保持了整体事务的一致性。如果想让方法一和方法二分别维护事务一致性怎么办?刚才说了@Transactional注解如果没有用代理调用,是不会生效的,所以必须用代理调用来实现,然后让方法1和方法2分别开启新的事务,就可以了没关系。把图片放在下面。在这两种情况下,方法1和方法2都在单独的事务中,每个都保持事务的一致性。接下来为了进一步优化,可以在transactionalMethod方法中分别控制方法1和方法2。如果你想最大限度地发挥代码的艺术,让我们假装开始吧。代码太长,超出屏幕。粘贴的抠图,红框标注需要仔细阅读。希望不会影响您的阅读体验。至此,本篇关于@Transactioinal注解的使用到此结束。简单总结一下:1.@Transactional注解保证每个方法都在一个事务中。如果有尝试,则必须在catch中抛出运行时异常。2.方法必须是public修饰符。否则注解不会生效,但是加注解也没有错,不会报错,只是没有用。3、调用该方法时,被调用方法上的注解不会生效,因为不能再次进行切面增强。如果有更详细的讨论,欢迎评论,感谢阅读。版权声明:本文为CSDN博主“范学博”原创文章,遵循CC4.0BY-SA版权协议,转载请附上原文出处链接及本声明。