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

2023-11-15

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,请求后结果如下:
在这里插入图片描述

注意:问:根据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,请求后结果如下:
在这里插入图片描述

注意 提问:根据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方法:

代码,更改如下:
在这里插入图片描述


param=b,请求后结果如下:
在这里插入图片描述
如图

结论:本类中,当一个没有开启事务的方法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注解:

请求后结果如下:
在这里插入图片描述

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注解:

请求后结果如下:
在这里插入图片描述

注意 提问:根据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的什么东西)。

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spirng的事务 方法A调用方法B,事务是否失效 的相关文章

随机推荐

  • Data Matrix代码效率增强!条码组件TBarCode SDK最新版更新啦!

    TBarCode SDK一款多功能的条形码生成器和打印软件 适用于Microsoft Office用户和软件开发人员 您可以创建和打印所有用于工业和商业用途的条形码符号 使用TBarCode SDK 您可以使用条形码生成器软件 它已经在无数
  • 我是这样来做破解qq,做QQ外挂的 【-】

    file 2005beta2 IQQData IQQCore IDynamicData txt brief 2005beta2 IQQData IQQCore IDynamicData txt v1 0 2005 09 08 23 58 1
  • Diffusion Model原理详解及源码解析

    作者简介 秃头小苏 致力于用最通俗的语言描述问题 专栏推荐 深度学习网络原理与实战 近期目标 写好专栏的每一篇文章 支持小苏 点赞 收藏 留言 文章目录 Diffusion Model原理详解及源码解析 写在前面 Diffusion Mod
  • Spring Boot 实现多文件上传

    文件上传 Spring Boot代码 代码结构 Controller层 package com yqifei upload controller import io swagger annotations Api import org sp
  • php结合swoole 如何对接ChatGPT接口

    对接ChatGPT接口需要进行以下步骤 安装Swoole扩展和PHP cURL扩展 在项目中引入 composer require guzzlehttp guzzle 来安装 Guzzle HTTP客户端 编写HTTP请求代码调用ChatG
  • 面向对象的三大特性:继承、多态、封装--封装

    一 封装 1 广义的封装 实例化一个对象 给对象空间封装一些属性 class A def init self name selef name name 对象封装属性 2 狭义的封装 私有制 私有成员 私有静态字段 私有动态方法 私有对象 私
  • Basic Level 1078 字符串压缩与解压 (20分)

    题目 文本压缩有很多种方法 这里我们只考虑最简单的一种 把由相同字符组成的一个连续的片段用这个字符和片段中含有这个字符的个数来表示 例如 ccccc 就用 5c 来表示 如果字符没有重复 就原样输出 例如 aba 压缩后仍然是 aba 解压
  • GDB中应该知道的几个调试方法

    七 八年前写过一篇 用GDB调试程序 于是 从那以后 很多朋友在MSN上以及给我发邮件询问我关于GDB的问题 一直到今天 还有人在问GDB的相关问题 这么多年来 有一些问题是大家反复在问的 一方面 我觉得我以前的文章可能没有说清楚 另一方面
  • .bat 文件是什么?做什么用的?

    一 bat文件是dos下的批处理文件 批处理文件是无格式的文本文件 它包含一条或多条命令 它的文件扩展名为 bat 或 cmd 在命令提示下输入批处理文件的名称 或者双击该批处理文件 系统就会调用cmd exe按照该文件中各个命令出现的顺序
  • 近年来,学习图像去雾不得不看的论文和源代码

    S G Narasimhan and S K Nayar 多幅图像 同一场景不同时间 天气 去雾 主页 NASA Retinex理论增强 主页 Ana Bel n Petro总结了NASA的Retinex理论 源代码 不过不是matlab版
  • HDU--1050:Moving Tables (贪心)

    1 题目源地址 http acm hdu edu cn showproblem php pid 1050 2 解题思路 将每个输入的起始房间和结束房间转换为房间前面的区间 按区间起始位置从小到大排序 首先取第一个区间 后续如果有区间的起始位
  • 计算机网络-第一章 计算机网络体系结构(详细知识点总结)

    目录 第一章 计算机网络体系结构 大纲 1 1 计算机网络概述 1 1 1 计算机网络的概念 1 1 2 计算机网络的组成 1 1 3 计算机网络的功能 1 1 4 计算机网络的分类 1 1 5 计算机网路的标准化工作及相关组织 1 1 6
  • 编译mediastreamer2/ffmpeg/linphone(x86平台)

    在x86环境下编译mediastreamer2的步骤 1 编译OGG库 音频编解码 http www xiph org downloads configure prefix usr disable static 2 编译SPEEX 音频编解
  • java对象比较器_具有多个字段的对象的Java比较器

    我有Collection5个字段的对象 id entityType entityId brandId productId 要排序的ArrayList的Collection我写了下面Comparaor Comparator collectio
  • uint是什么数据类型_「IEC 61131-3」标准数据类型

    IEC 61131 3定义的标准数据类型有 布尔型 整型 浮点型 字符串型 时间日期类型和常数 以下将逐一介绍各种数据类型 布尔型 布尔变量可取值TRUE和FALSE 占8位内存空间 布尔型变量声明示例 bExec BOOL FALSE b
  • 无法启动此程序,因为计算机中丢失QT5Core.dll

    背景 QT项目从QTCreator移植到VS2010中时出现这个问题 原因 在Qt Creator中运行时会根据你当前选择的构建套件生成一套自己的环境变量 这套环境变量与当前电脑的环境变量的差别是添加了Qt库的引用路径 所以在Creator
  • 取余运算的规则

    取余运算满足以下规则 x y p x p y p p 证明如下 假设 x a p b y c p d 则 x p b y p d 则 x y p a p b c p d p a c p b d p b d p x p y p p
  • JAVASE总复习

    一 填空题 共 20 个题目 总计 20 分 1 Java application 中的主类需要包含 main 方法 main 方法的返回类型是void 2 移位运算符可以起到对操作数乘以 2 或者除以 2 的作用 那么操作数除以 2 的移
  • shader练习中遇到的问题点

    half3 lrDirWS normalize reflect lDirWS nDirWS 不加normalize会有白点点 1 max 0 ndotv 模型上会出现黑点点 max 0 1 ndotv 模型上不会出现黑点点
  • Spirng的事务 方法A调用方法B,事务是否失效

    Springboot开启了事务的方法调用没有事务的方法 提示 上方标题是一个很笼统的场景 详情展开如下 先说结论 总结 方法A调用方法B 场景一 如果A和B方法在同一个类中 如果A加 Transactional注解 B加不加 Transac