Spring的事务 方法A调用方法B,事务是否失效

这篇具有很好参考价值的文章主要介绍了Spring的事务 方法A调用方法B,事务是否失效。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Springboot开启了事务的方法调用没有事务的方法:
提示:上方标题是一个很笼统的场景,详情展开如下,先说结论:

总结:
方法A调用方法B:
场景一 如果A和B方法在同一个类中:
如果A加@Transactional注解,B加不加@Transactional注解,事务是有效的,则AB在同一事务中。
如果A不加@Transactional注解,B加不加@Transactional注解,事务都是无效的。(但是如果使用代理类来进行b方法调用,那么B会开启事务,A不会开启事务

场景二 如果A和B不在同一个类中:
如果A加@Transactional注解,B加不加@Transactional注解,事务是有效的。
如果A不加@Transactional注解,B加了@Transactional注解,只有B是有事务的;
如果A不加@Transactional注解,B不加@Transactional注解,A、B都是没有事务的。

或者这样理解
1、如果A加@Transactional注解,不管是不是在一个类中,不管B加不加注解,AB都是在同一事务中;
2、如果A不加@Transactional注解,只有B加@Transactional注解,AB方法为同一类,事务失效(使用代理类,B方法有事务);AB不同类,只有B有事务;
3、如果A不加@Transactional注解,B不加@Transactional注解,则没有事务;

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

注意:代码中TestUserServiceImpl的update方法updateOther方法,在全文中简称为A方法

场景一 A和B方法在同一个类中:

我们先自己建立一个controller,service,以及mapper。代码如下

@RestController
@RequestMapping("/testUser")
public class TestShiWuController {

    @Autowired
    private ITestService testService;
    
    @GetMapping("/update")
    public void update(String param) {
        testService.update(param);
    }
}
@Service
public class TestUserServiceImpl implements ITestService {

    @Autowired
    private TestUserMapper testUserMapper;

    @Override
    @Transactional(rollbackFor =Exception.class)
    public int update( String param) {

        testUserMapper.updateUserById("a",1L);
        b(param);
        c(param);
        return 0;
    }

//    @Transactional(rollbackFor =Exception.class)
    public void b(String s) {
        TestUser testUser = testUserMapper.selectById(1L);
        System.out.println(testUser);
        testUserMapper.updateUserById("b",2L);
        if (s.equals("b")) {
            int a = 1/0;
        }
    }

//    @Transactional(rollbackFor =Exception.class)
    public void c(String s) {

        TestUser testUser = testUserMapper.selectById(2L);
        System.out.println(testUser);
        testUserMapper.updateUserById("c",3L);
        if (s.equals("c")) {
            int a = 1/0;
        }
    }
}
@Mapper
public interface TestUserMapper extends BaseMapper<TestUser> {
    @Update("update testUser set name = #{name} where id=#{id}")
    public int updateUserById(String name, Long id );
}

1.1 如果A加@Transactional注解,B不加@Transactional注解:

param=b,请求后结果如下:
事务a调用事务b,事务b出问题,事务生效了嘛,springboot,java,spring,spring boot,mysql

注意:问:根据idea控制台可看出,b方法进行查询了id=1的记录,为什么name=a而不是name=1呢?。
答:此时查询出来的记录name=a了 ,是因为b在a的事务中了,同一事务内数据是可见共享的,而此时的name=a并未落到数据库中。为了验证此处,可以在a方法最后添加一个Thread.Sleep(10000)睡眠10s,在这10s中调用接口传参param=a,,去navicat查询数据库中id=1的这条记录,查看是否为1,若为1则证明我上述是正确的!

原因分析:

提示:由此可见,b和c方法加入了a方法的主事务中,即三个方法在同一个事务中:

解析:在param=b调用接口后,可以直观的看到,数据都变成了初始值,即回滚了。(其实b方法加不加@Transional注解,事务都会生效即两个方法都在同一个事务中,博主亲测)

1.2 如果update方法不加@Transactional注解,B加@Transactional注解:

代码,更改如下:

param=b,请求后结果如下:
事务a调用事务b,事务b出问题,事务生效了嘛,springboot,java,spring,spring boot,mysql

注意 提问:根据idea控制台可看出,b方法进行查询了id=1的记录,此时name=a和上方1.1标题的name=a有什么区别吗 ?。
答:此时查询出来的记录name=a ,是因为update方法的更改已经落到数据库了,与事务没有关系。为了验证此处,可以在a方法最后添加一个Thread.Sleep(10000)睡眠10s,在这10s中调用接口传参param=a,,去navicat查询数据库中id=1的这条记录,查看name是否为a,若10s内name=a则证明我上述是正确的!

1.3 如果update方法不加@Transactional注解,B加@Transactional注解,并且使用代理类调用B方法:

代码,更改如下:
事务a调用事务b,事务b出问题,事务生效了嘛,springboot,java,spring,spring boot,mysql


param=b,请求后结果如下:
事务a调用事务b,事务b出问题,事务生效了嘛,springboot,java,spring,spring boot,mysql
如图

结论:本类中,当一个没有开启事务的方法A:

1.直接调用开启了事务的方法B,AB都不会开启事务。
2.但是如果A方法使用代理对象进行B方法的调用,那么B方法还是会开启事务的,但是A方法依然没有事务。

场景一的总结分析:

解析:
情景1.2 当param=b的时候,可以观察到id=1,id=2的记录并未回滚。由此可见,update方法没有开启事务,b方法的事务也失效了。
情景1.3 当param=b的时候,可以观察到id=1,的记录并未回滚。id=2的记录 进行了数据回滚。由此可见,A方法没有开启事务,B方法的开启了事务。
问: 为什么会出现上述情况呢?
答: 因为本类中,A方法直接进行B方法的调用相当于使用this.b(),具体看下方贴图class编译文件。这里涉及到@Transitional的事务原理是基于AOP实现的,所以要想事务生效,必须通过代理对象进行方法的调用,this调用相当于原生对象调用(未被事务代理进行方法增强)

备注:我的开发环境是springboot 2.3版本,AOP默认代理是Cglib,可以进行代理对象直接调用本类方法,无需implements接口,也可以直接进行ServiceImpl的强制类型转换。(如果是jdk代理会报错)当然,也可以使用自己注入自己的方式,进行B方法的调用,但是要通过Implements的方式。


场景二 A和B方法不在同一个类中:

注意:
TestUserServiceImpl的updateOther 就是A方法
GoodsServiceImpl的updateGoodsById 就是B方法
我们先自己建立一个另一个操作类,用于观察对比方法对应的事务具体情况(事务传播等级)。代码如下

@Service
public class TestUserServiceImpl implements ITestService {

    @Autowired
    private TestUserMapper testUserMapper;
    @Autowired
    private IGoodsService goodsService;
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateOther(String param) throws InterruptedException {
        // 该testUser表记录,修改前 name = '1'
        testUserMapper.updateUserById("a", 1L);
        // 该goods表记录,修改前 name = '1'
        goodsService.updateGoodsById("product1", 1L);
        return 1;
    }
}
@Service
public class GoodsServiceImpl implements IGoodsService {

    @Autowired
    private GoodsMapper goodsMapper;
    @Autowired
    private TestUserMapper testUserMapper;

//    @Transactional(rollbackFor = Exception.class)
    @Override
    public int updateGoodsById(String name, Long id) {
        TestUser testUser = testUserMapper.selectById(1L);
        System.out.println("GoodsServiceImpl.updateGoodsById()查询的user结果: " + testUser.toString());
        goodsMapper.updateGoodsById(name, id); //          修改前 name = '1'
        int i = 1 / 0;
        return 0;
    }
}
@Mapper
public interface GoodsMapper extends BaseMapper<Goods> {
    @Update("update goods set name = #{name} where id=#{id}")
    public int updateGoodsById(String name, Long id );
}

2.1 如果A方法加@Transactional注解,b方法不加@Transactional注解:

请求后结果如下:
事务a调用事务b,事务b出问题,事务生效了嘛,springboot,java,spring,spring boot,mysql

2.1的现象分析:param=b的时候,mysql中的数据都未改变,说明两者都进行了事务数据回滚,说明二者都开启了事务。并且b方法中查询到了a方法的数据改变,说明二者是同一个事务。
结论:当不同类方法调用的时候(A调用B),若A方法添加了@Transional,B方法没有添加@Transional,那么B方法会加入到A方法的事务中。

(若B方法添加了@Transional,AB方法也在同一事务中,因为spring的事务默认传播等级)

2.2 如果A方法不加@Transactional注解,B加@Transactional注解:

请求后结果如下:
事务a调用事务b,事务b出问题,事务生效了嘛,springboot,java,spring,spring boot,mysql

注意 提问:根据idea控制台可看出,b方法进行查询了id=1的记录,此时name=a和上方2.1标题的name=a有什么区别吗 ?。
答:
2.2场景 b方法查询的name=a是因为A方法没有开启事务,数据update更改后直接落库了,此时查询出来的name=a,是从数据库中读取的。
2.1场景 b方法查询的name=a是因为二者在同一事务中,数据共享的,好像是什么快照读(具体忘记了,mvcc的什么东西)。文章来源地址https://www.toymoban.com/news/detail-795759.html

2.2的现象分析:因为在b方法中出现了异常,mysql中只有goods表进行了数据回滚,说明只有B方法开启了事务。
结论:在不同类中,A不添加@Transitional方法调用B添加@Transitional的方法,A不会开启事务,B会开启事务。

———————————————— 版权声明:本文开头部分为借鉴下方链接 原文链接:https://blog.csdn.net/u012279452/article/details/126505417

到了这里,关于Spring的事务 方法A调用方法B,事务是否失效的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • AopContext.currentProxy的原理和实战(解决同一个类中方法嵌套事务的失效问题)

    @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) public class TransactionTest {       /**      * 方法A没事务      *      * @param      * @return void      */     public String A(){         System.out.println(\\\"A方法执行开始!!!\\\");         //生成TransactionTest代理类,再调用B方法,B的事

    2024年02月01日
    浏览(76)
  • spring动态代理失效,AOP失效,事务@Transactional失效原因

    事务基于@Transactional注解和AOP(动态代理) 对于基于接口动态代理的 AOP 事务增强来说,由于接口的方法都必然是 public 的,这就要求实现类的实现方法也必须是 public 的(不能是 protected、private 等),同时不能使用 static 的修饰符。所以,可以实施接口动态代理的方法只能是

    2024年02月15日
    浏览(48)
  • Spring高手之路-Spring事务失效的场景详解

    目录 前言 @Transactional 应用在非 public 修饰的方法上 同一个类中方法调用,导致@Transactional失效 final、static方法 @Transactional的用法不对 @Transactional 注解属性 propagation 设置不当 @Transactional注解属性 rollbackFor 设置错误 用错注解 异常被捕获 数据库引擎不支持事务 Spring中比较容易

    2024年01月18日
    浏览(39)
  • Spring 事务失效的八种场景

    原因:Spring 默认只会回滚非检查异常 解法:配置 rollbackFor 属性 @Transactional(rollbackFor = Exception.class) 原因:事务通知只有捉到了目标抛出的异常,才能进行后续的回滚处理,如果目标自己处理掉异常,事务通知无法知悉 解法1:异常原样抛出: 在 catch 块添加 throw new RuntimeExc

    2024年02月14日
    浏览(39)
  • 解决Spring事务不生效的10种常见场景

    Spring提供了非常方便的事务管理功能,可以让我们轻松地进行事务的提交、回滚等操作。然而,在实际的开发过程中,有时候我们会遇到Spring事务不生效的情况,这个时候我们就需要进行排查和调试,找出问题所在。 Spring事务管理需要在配置文件中进行相应的配置,如果没有

    2023年04月22日
    浏览(38)
  • 遇到Spring事务失效,你该怎么办?

    Spring 事务场景失效是一个常见的问题。今天来分析这个问题。 失效原因 事务方法被final、static修饰:这是因为Spring事务的实现依赖于AOP技术,而final、static方法无法被代理,因此在这些方法中调用事务方法,事务无法生效。 方法访问权限不是public:Spring事务的实现也

    2023年04月15日
    浏览(48)
  • Spring中事务失效的8中场景 对于一个事务开子线程

    1. 数据库引擎不支持事务 这里以 MySQL为例,MyISAM引擎是不支持事务操作的,一般要支持事务都会使用InnoDB引擎,根据MySQL 的官方文档说明,从MySQL 5.5.5 开始的默认存储引擎是 InnoDB,之前默认的都是 MyISAM,所以这一点要值得注意,如果底层引擎不支持事务,那么再怎么设置也

    2024年02月16日
    浏览(39)
  • Spring事务传播机制、实现方式、失效场景即原理

    贴一篇源码分析的好文章:https://blog.csdn.net/qq_30905661/article/details/114400417 一个事务对应一个数据库连接。 通过 this 来调用某个带有 @Transactional 注解的方法时,这个注解是失效的 spring事务底层是通过数据库事务和AOP实现的 首先对于使用@Transactional的注解的bean,spring会创建一个

    2024年02月14日
    浏览(36)
  • spring中事务失效的情况(常见的5种)

    1.多线程调用 从上面的例子中,我们可以看到事务方法add中,调用了事务方法doOtherThing,但是事务方法doOtherThing是在另外一个线程中调用的。 这样会导致两个方法不在同一个线程中,获取到的数据库连接不一样,从而是两个不同的事务。如果想doOtherThing方法中抛了异常,ad

    2024年02月13日
    浏览(33)
  • 揭秘Spring事务失效场景分析与解决方案

    在Spring框架中,事务管理是一个核心功能,然而有时候会遇到事务失效的情况,这可能导致数据一致性问题。本文将深入探讨一些Spring事务失效的常见场景,并提供详细的例子以及解决方案。 场景: 当一个事务方法内部调用另一个方法,而被调用的方法没有声明为 @Transact

    2024年02月02日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包