事务注解@Transactional失效的情况解析

news/2024/11/8 23:13:15 标签: java

文章目录

  • 前言
  • 1. 动态代理无法实现
  • 2. Spring管理不当
  • 3. 事务遇到异常未回滚

前言

理解@Transactional注解失效的情况,先要谈Spring中注解的实现原理,是以动态代理的形式出现的,如果无法实现动态代理,注解自然就无法生效。另外,如果类没有被Spring管理,其上的@Transactional注解也不会生效。此外,如果执行事务的过程中遇到异常却没有回滚处理,不同事务之间的传播机制的问题,或者Spring管理不正确也会导致失效。

因此,注解@Transactional失效的情况可以分以下几类:

  1. 动态代理无法实现
  2. Spring管理不当
  3. 事务遇到异常未回滚

接下来将分别进行介绍。

1. 动态代理无法实现

首先,搞清楚动态代理的实现方式有两种:

  • 要代理的对象实现了接口:使用JDK Proxy类来创建代理对象。
  • 要代理的对象没有实现接口:使用Cglib生成被代理对象的子类来代理。

而动态代理无法实现的,进一步导致@Transactional注解失效的情况有以下几种:

  1. 方法自调用(也就是递归):通过this引用完成,绕过了代理。

    • 示例:
      java">@Service
      public class MyService {
          
          @Transactional
          public void methodA() {
              // 业务逻辑
              methodA(); // 自调用,不会应用事务
          }
      }
      
    • 解决方法:methodA() 是被标记为 @Transactional 的方法,事务在调用 recursiveMethod() 时保持生效。
      java">@Service
      public class MyService {
      
          @Transactional
          public void methodA() {
              // 业务逻辑
              recursiveMethod(0); // 开始递归
          }
      
          private void recursiveMethod(int count) {
              if (count < 5) {
                  // 递归逻辑
                  System.out.println("Count: " + count);
                  recursiveMethod(count + 1); // 继续递归
              }
          }
      }
      
  2. 同一个类中方法之间的调用:

    • 示例:
      java">@Service
      public class MyService {
          
          @Transactional
          public void outerMethod() {
              innerMethod(); // 事务不会生效
          }
      
          public void innerMethod() {
              // 业务逻辑
          }
      }
      
    • 解决方法:将 innerMethod() 提取到另一个 Spring 管理的 Bean 中,然后通过依赖注入的方式调用它。
      java">@Service
      public class MyService {
      
          @Autowired
          private MyService myService; // 注入同一个服务
      
          @Transactional
          public void outerMethod() {
              myService.innerMethod(); // 通过代理调用
          }
      
          public void innerMethod() {
              // 业务逻辑
          }
      }
      
  3. 方法的修饰符不是public:由于只有公共的方法才会被代理,如果方法是私有的(private)或保护的(protected),它们不会被 Spring 的代理机制拦截,因此 @Transactional 注解不会生效。

    • 示例:
      java">@Service
      public class MyService {
      
          // 私有方法,@Transactional 注解将失效
          @Transactional
          private void myPrivateMethod() {
              // 业务逻辑
              System.out.println("Executing private method");
          }
      
          public void execute() {
              myPrivateMethod(); // 自调用,且私有方法不会被代理
          }
      }
      
    • 解决办法:
      java">@Service
      public class MyService {
      
          // 将方法改为 public,确保事务生效
          @Transactional
          public void myPublicMethod() {
              // 业务逻辑
              System.out.println("Executing public method");
          }
      
          public void execute() {
              myPublicMethod(); // 通过代理调用,事务将生效
          }
      }
      

2. Spring管理不当

  1. 调用对象不是Spring管理的bean,比如new
    • 示例:
      java">public class MyClass {
          public void myMethod() {
              MyService service = new MyService(); // 直接创建,不是 Spring 管理的
              service.methodA(); // 不会应用事务
          }
      }
      
    • 解决办法:
      java">@Component
      public class MyClass {
      
          @Autowired
          private MyService myService; // 通过 Spring 注入
      
          public void myMethod() {
              myService.methodA(); // 通过 Spring 管理的对象调用,事务将生效
          }
      }
      

3. 事务遇到异常未回滚

  1. @Transactional 注解默认情况下只会在遇到未检查的异常(如 RuntimeException)时回滚事务。如果抛出的是检查异常(如 IOException),则事务不会回滚。

    • 示例:
      java">@Transactional
      public void myMethod() throws IOException {
          throw new IOException(); // 不会导致事务回滚
      }
      
    • 解决方法:
      java">@Transactional(rollbackFor = IOException.class) // 指定检查异常
      public void myMethod() throws IOException {
          throw new IOException(); // 将导致事务回滚
      }
      
  2. required修饰的方法outerMethod()调用required_new修饰的方法innerMethod():在这种情况下,方法innerMethod()的异常不会导致方法outerMethod()的事务回滚,从而导致数据不一致。

    • 示例:
      java">@Service
      public class OuterService {
      
          @Transactional
          public void outerMethod() {
              // 调用内部方法
              innerMethod(); // 使用 REQUIRES_NEW
          }
      
          @Transactional(propagation = Propagation.REQUIRES_NEW)
          public void innerMethod() {
              // 业务逻辑
              throw new RuntimeException("Exception in innerMethod"); // 仅会回滚 innerMethod 的事务
          }
      }
      
    • 解决办法:
      java">@Transactional
      public void outerMethod() {
          try {
              innerMethod(); // 处理结果
          } catch (RuntimeException e) {
              // 处理 innerMethod 的异常
              System.out.println("Handled inner exception: " + e.getMessage());
          }
      }
      
  3. required修饰的方法outerMethod()调用nested修饰的方法innerMethod():如果 innerMethod() 抛出异常,innerMethod() 的子事务将回滚,但 outerMethod() 的外部事务仍然可以继续执行。

    • 示例:
      java">@Service
      public class OuterService {
      
          @Transactional
          public void outerMethod() {
              // 调用内部方法
              innerMethod(); // 使用 NESTED
          }
      
          @Transactional(propagation = Propagation.NESTED)
          public void innerMethod() {
              // 业务逻辑
              throw new RuntimeException("Exception in innerMethod"); // 事务将回滚
          }
      }
      
    • 解决办法:
      java">@Transactional
      public void outerMethod() {
          try {
              innerMethod(); // 可能会抛出异常
          } catch (RuntimeException e) {
              // 处理异常,可能继续执行其他逻辑
              // 或者决定是否回滚 outerMethod 的事务
              System.out.println("Handled inner exception: " + e.getMessage());
          }
      }
      

以上为个人学习分享,如有问题,欢迎指出:)


http://www.niftyadmin.cn/n/5744539.html

相关文章

【数据集】【YOLO】【目标检测】道路结冰数据集 1527 张,YOLO目标检测实战训练教程!

数据集介绍 【数据集】道路结冰数据集 1527 张&#xff0c;目标检测&#xff0c;包含YOLO/VOC格式标注。数据集中包含2种分类&#xff1a;“clear_road, ice_road”。数据集来自国内外图片网站和视频截图&#xff0c;部分数据经过数据增强处理。检测范围监控视角检测、无人机视…

Vue 计算属性和监听器

文章目录 一、计算属性1. 计算属性定义2. computed 比较 methods3. 计算属性完整写法 二、监听器1. 普通监听2. 添加额外配置项 一、计算属性 1. 计算属性定义 概念&#xff1a;基于现有的数据&#xff0c;计算出来的新属性&#xff0c;依赖的数据变化&#xff0c;自动重新计…

(十二)JavaWeb后端开发——MySQL数据库

目录 1.数据库概述 2.MyQSL 3.数据库设计 DDL 4.MySQL常见数据类型 5.DML 1.数据库概述 数据库&#xff1a;DataBase(DB)&#xff0c;是存储和管理数据的仓库 数据库管理系统&#xff1a;DataBase ManagementSystem(DBMS)&#xff0c;操纵和管理数据库的大型软件 SQL&a…

SSH实验5密钥登录Linuxroot用户(免密登录)

当用户尝试通过SSH连接到远程服务器时&#xff0c;客户端会生成一对密钥&#xff1a;公钥和私钥。公钥被发送到远程服务器&#xff0c;并存储在服务器的~/.ssh/authorized_keys文件中。而私钥则由客户端保管&#xff0c;不会传输给服务器。 在连接过程中&#xff0c;客户端使用…

案例解读 | 某大型家居企业综合运维监控平台建设实践

01.客户简介 案例客户是一家在A股上市的大型家居企业&#xff0c;专注于客餐厅、卧室及全屋定制家居产品的研究、开发、生产和销售&#xff0c;旗下拥有多个系列自有品牌&#xff0c;并与美国、意大利家居品牌开展战略合作&#xff0c;业务覆盖全球120余个国家和地区&#xff…

中肿团队提出的“免疫三明治”(放疗+化疗+免疫治疗),成功登上柳叶刀肿瘤|顶刊精析·24-11-08

小罗碎碎念 该研究首次发现在同期放化疗基础上增加特瑞普利单抗&#xff08;PD-1抗体&#xff09;新辅助和辅助治疗显著提高了高危局部晚期鼻咽癌患者生存率。 如果大家看完这篇推送以后&#xff0c;有什么好的医工交叉点子&#xff0c;欢迎和我一起探讨&#xff01;&#xff0…

【YOLO学习】YOLOv10配置

文章目录 1. 环境配置步骤2. 国内镜像源3. 安装pytorch 1. 环境配置步骤 1. YOLOv10官网&#xff1a;https://github.com/THU-MIG/yolov10 2. 创建环境&#xff1a; conda create -n yolov10 python3.9 conda activate yolov10 pip install -r requirements.txt pip install -…

java: 无法访问org.springframework.web.bind.annotation.RequestMapping

一、报错问题 java: 无法访问org.springframework.web.bind.annotation.RequestMapping 二、原因分析 SpringBoot使用了3.0或者3.0以上&#xff0c;因为Spring官方发布从Spring6以及SprinBoot3.0开始最低支持JDK17。所以仅需要将SpringBoot版本降低为3.0以下即可&#xff08;或…