Spring事务失效,同一个类中的内部方法调用事务失效

声明式事务实现方式

所谓声明式事务,也就是通过配置的方式,比如通过配置文件xml或者注解的方式,来告诉Spring哪些方法需要Spring帮忙管理事务,然后开发者只需要关注业务代码,而事务的事情则由Spring自动帮我们控制。

配置文件的方式:即在spring.xml文件中进行统一配置,开发者基本不用关注事务的事情了,一切交给spring处理。

基于注解的方式:只需要在想要添加事务的方法上加上@Transaction注解就可以了。

实际工作中,基于配置文件的方式用的很少,我们工作中主要还是用注解的方式较多。

注解方式实现原理

简单介绍一下原理

当spring容器启动的时候,发现有@EnableTransactionManagement注解,此时会拦截所有bean的创建,然后会扫描一下bean上是否有@Transaction注解(类、接口、或者方法上有这个注解都可以),如果有这个注解,spring会通过aop的方式给这个bean生成代理对象(代理对象中存在本类对象),代理对象中会增加一个拦截器,拦截器会拦截bean中public方法的执行,会在方法执行前启动事务,方法执行完毕之后提交或者回滚事务。

同一个Service类中,方法B上有事务注解,方法A上没有,在A中调用B方法,事务会失效

其实仔细看了上面的原理,也就很好解释了为什么会失效了。

首先,外部调用A方法时,是通过代理对象来调用的,如ProxyService.A(),而我们在A方法中调用B方法是通过本类对象Service来实现的,即Service.B(),因为代理对象中存在Service对象。所以这就会导致事务失效。

@Service
class A{
    @Transactinal
    method b(){...}
    
    method a(){    //标记1
        b();
    }
}
 
//Spring扫描注解后,创建了另外一个代理类,并为有注解的方法插入一个startTransaction()方法:
class proxy$A{
    A objectA = new A();
    method b(){    //标记2
        startTransaction();
        objectA.b();
    }
 
    method a(){    //标记3
        objectA.a();    //由于a()没有注解,所以不会启动transaction,而是直接调用A的实例的a()方法
    }
}

当我们调用A的bean的a()方法的时候,也是被proxy$A拦截,执行proxy$A.a()(标记3),然而,由以上代码可知,这时候它调用的是objectA.a(),也就是由原来的bean来调用a()方法了,所以代码跑到了“标记1”。由此可见,“标记2”并没有被执行到,所以startTransaction()方法也没有运行。

必须通过代理对象来调用方法,事务才会生效。

注意代理对象最终都是要调用原始对象的,而原始对象去调用方法时,是不会再触发代理了。

方法A调用方法B事务失效分析

如果A和B两个方法在同一个类中

1、如果A加了@Transaction注解,B上有没有@Transaction注解,事务都是有效的,则AB在同一个事务中。

2、如果A不加@Transaction注解,B上有没有@Transaction注解,事务都是无效的

如果A和B两个方法不在同一个类中

1、如果A加了@Transaction注解,B上有没有@Transaction注解,事务都是有效的。

2、如果A不加@Transaction注解,B加了@Transaction注解,只有B是有事务的。

3、如果A不加@Transaction注解,B也不加@Transaction注解,A和B都是没有事务的。

简单理解:

只要A加了@Transaction注解,那么无论是不是在同一个类中,无论B加不加注解,AB都在同一个事务中。

A不加注解,B加了注解,如果AB在同一个类中,则事务失效;如果AB不同类,则只有B有事务

原因分析:在A方法有@Transaction注解时,spring在管理的时候会生成一个代理类,在外部调用A方法时,实际执行的是代理类里面的方法,该代理类里面的方法已经包括了B方法的调用,已经成为了一个方法,所以事务是有效的。

事务失效的常见情况

1、方法不是public类型的

@Transaction注解可以用在类、接口和方法上,如果将@Transaction注解用在了非public方法上,事务将失效

因为spring事务底层是通过cglib动态代理来实现的,而cglib动态代理是基于父子类来实现的,我们知道,子类是不能重载父类的private方法的,所以在private方法上面加@Transaction会导致事务失效。

2、向上述所说的方法调用场景

3、异常类型错误

spring事务的回滚机制:对业务方法进行try-catch,如果捕获到有指定的异常时,spring会自动对事务进行回滚。

那么问题来了,哪些异常会让spring自动回滚事务呢?并不是任何异常情况下,spring都会回滚事务的,默认情况下,RuntimeException和Error异常情况下,spring才会回滚事务。当然也可以自定义回滚的异常类型。

  • 7
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
当在同一个类中方法相互调用时,如果希望事务能够生效,可以采取以下解决办法: 1. 使用代理调用方法:由于Spring事务管理是通过AOP代理实现的,所以可以通过使用代理对象调用方法来触发事务管理。可以通过将方法调用委托给代理对象来确保事务的生效。 2. 将被调用方法抽取到另一个类中:将被调用方法抽取到另一个类中,并确保在被调用方法上添加@Transactional注解。这样,在调用方法调用被抽取的方法时,事务将能够生效。 3. 使用AspectJ模式的事务管理:Spring还提供了AspectJ模式的事务管理,可以在同一个类中方法相互调用时保持事务的生效。通过配置AspectJ的切面来实现事务的管理,可以细粒度地控制事务的传播行为和回滚条件。 需要注意的是,以上解决办法需要根据具体情况选择合适的方式,并确保在调用方法上正确地添加@Transactional注解。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Spring同一个service中方法相互调用事务不生效问题解决方案](https://blog.csdn.net/a1036645146/article/details/107469578)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值