Spring 事务不生效的几种场景

news2024/11/19 21:17:33

Spring 事务不生效的几种场景

详细内容参考以下链接,这个链接是原文

spring 事务不生效的15中场景

非原创。

以下内容只是为了学习,加深印象,仅作为个人学习笔记, 请支持原创,内容请点击 spring 事务不生效的15中场景

-------------------------------------------------------------------------------分割线-----------------------------------------------------------------------

整理如下几种Spring事务失效的场景

在这里插入图片描述

1、service类没有被Spring管理

//@Service (注释了@Service 或者 忘记添加注解)
public class xxxImpl implements xxxService {

    @Autowired
    private xxMapper xxMapper;
    
     @Autowired
    private yyMapper yyFlowMapper;

    @Transactional
    public void addTianLuo(Domain domain) {
        //do something
        xxMapper.save(domain);
        //do other something
        yyFlowMapper.saveFlow(domain);
    }
}
  • 事务不生效的原因:上面例子中, @Service注解注释之后,spring事务(@Transactional)没有生效,因为Spring事务是由AOP机制实现的,也就是说从Spring IOC容器获取bean时,Spring会为目标类创建代理,来支持事务的。但是@Service被注释后,你的service类都不是spring管理的,那怎么创建代理类来支持事务呢
  • 解决方案:加上@Service注解。

2.没有在Spring配置文件中启用事务管理器

以下案例只针对非Spring Boot 项目

@Configuration
public class AppConfig {
    // 没有配置事务管理器
}

@Service
public class MyService {
    @Transactional
    public void doSomething() {
        // ...
    }
}
  • 事务不生效的原因:没有在AppConfig中配置事务管理器,因此Spring无法创建事务代理对象,导致事务不生效。即使在MyService中添加了@Transactional注解,该方法也不会被Spring管理的事务代理拦截。
  • 解决方案:为了解决这个问题,应该在AppConfig中配置一个事务管器。例如:
@Configuration
public class AppConfig {
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }
}

@Service
public class MyService {
    @Transactional
    public void doSomething() {
        // ...
    }
}

3、事务方法被final、static、private关键字修饰

@Service
public class xxImpl implements xxxService {

    @Autowired
    private xxMapper xxMapper;
     

    @Transactional
    public final void addTianLuo(Domain domain) {
         
        xxMapper.save(domain); 
        
        xxMapper.saveOther(domain);
    }
}
  • 事务不生效的原因:如果一个方法被声明为final或者static,则该方法不能被子类重写,也就是说无法在该方法上进行动态代理,这会导致Spring无法生成事务代理对象来管理事务。
  • 解决方案addTianLuo事务方法不要用final修饰或者static修饰。

4、同一个类中,方法内部调用

@Service
public class xxServiceImpl implements xxService {

    @Autowired
    private xxMapper xxMapper;
   
    public void addTianLuo(Domain domain){
     // 调用内部的事务方法
     this.executeAddMethod(domain);
   }

    @Transactional
    public void executeAddMethod(Domain domain) {
         xxMapper.save(domain); 
        
        xxMapper.saveOther(domain);
    }
}
  • 事务不生效的原因: 事务是通过Spring AOP代理来实现的,而在同一个类中,一个方法调用另一个方法时,调用方法直接调用目标方法的代码,而不是通过代理类进行调用。即以上代码,调用目标executeAddTianLuo方法不是通过代理类进行的,因此事务不生效。
  • 解决方案:可以新建多一个类,让这两个方法分开,分别在不同的类中, 当然也可以注入自己,然后调用,这种方法不太优雅。

总结:

以上几种类型都是使用了声明式的@Transaction事务注解,从spring Aop 为事务类生成 代理对象的角度出发,当类没有被IOC管理、使用了不当的修饰符、没有使用spring 生产的代理对象的方法(内部方法直接调用)等导致事务无法生效。

6、数据库的存储引擎不支持事务

​ Spring事务的底层,还是依赖于数据库本身的事务支持。在MySQL中,MyISAM存储引擎是不支持事务的,InnoDB引擎才支持事务。因此开发阶段设计表的时候,确认你的选择的存储引擎是支持事务的

7、配置错误的 @Transactional 注解

@Transactional(readOnly = true)
public void updateUser(User user) {
    userDao.updateUser(user);
}
  • 事务不生效的原因:虽然使用了@Transactional注解,但是注解中的readOnly=true属性指示这是一个只读事务,因此在更新User实体时会抛出异常。
  • 解决方案:将readOnly属性设置为false,或者移除了@Transactional注解中的readOnly属性。

8.事务超时时间设置过短

@Transactional(timeout = 1)
public void doSomething() {
    //...
}

事务不生效的原因:在上面的例子中,timeout属性被设置为1秒,这意味着如果事务在1 秒内无法完成,则报事务超时了

9. 使用了错误的事务传播机制

@Service
public class xxServiceImpl {
 
    @Autowired
    private XXMapper xxMapper;
    @Autowired
    private XXFlowMapper xxFlowMapper;
 
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public  void doInsertTianluo(XX xx) throws Exception {
        xxMapper.save(xx);
        xxFlowMapper.saveFlow(xx);
    }
}
  • 事务不生效的原因Propagation.NOT_SUPPORTED传播特性不支持事务。
  • 解决方案:选择正确的事务传播机制。

帮大家复习一下,Spring提供了七种事务传播机制。它们分别是:

  • REQUIRED(默认):如果当前存在一个事务,则加入该事务;否则,创建一个新事务。该传播级别表示方法必须在事务中执行。
  • SUPPORTS:如果当前存在一个事务,则加入该事务;否则,以非事务的方式继续执行。
  • MANDATORY:如果当前存在一个事务,则加入该事务;否则,抛出异常。
  • REQUIRES_NEW:创建一个新的事务,并且如果存在一个事务,则将该事务挂起。
  • NOT_SUPPORTED:以非事务方式执行操作,如果当前存在一个事务,则将该事务挂起。
  • NEVER:以非事务方式执行操作,如果当前存在一个事务,则抛出异常。
  • NESTED:如果当前存在一个事务,则在嵌套事务内执行。如果没有事务,则按REQUIRED传播级别执行。嵌套事务是外部事务的一部分,可以在外部事务提交或回滚时部分提交或回滚。

10. rollbackFor属性配置错误

@Service
public class XXerviceImpl implements XXService {

    @Autowired
    private XXMapper xxMapper;
    
    @Autowired
    private XXFlowMapper xxFlowMapper;

    @Transactional(rollbackFor = Error.class)
    public void addTianLuo(XX xx) {
        //
        xxMapper.save(xx);
        //
        xxFlowMapper.saveFlow(xx);
        //模拟异常抛出
        throw new Exception();
    }
}
  • 事务不生效的原因: 其实rollbackFor属性指定的异常必须是Throwable或者其子类。默认情况下,RuntimeExceptionError两种异常都是会自动回滚的。但是因为以上的代码例子,指定了rollbackFor = Error.class,但是抛出的异常又是Exception,而Exception和Error没有任何什么继承关系,因此事务就不生效。

  • 解决方案rollbackFor属性指定的异常与抛出的异常匹配。

在这里插入图片描述

11.事务注解被覆盖导致事务失效

public class MyService {

    @Autowired
    private MyRepository myRepository;

    @Transactional
    public void doSomething(String data) {
        myRepository.save(data);
    }
}

public class MyTianluoService extends MyService {
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void doSomething(String data) {
        super.doSomething(data);
    }
}
  • 事务失效的原因MyTianluoServiceMyService的子类,并且覆盖了doSomething()方法。在该方法中,使用了不同的传播行为(REQUIRES_NEW)来覆盖父类的@Transactional注解。在这种情况下,当调用MyTianluoServicedoSomething()方法时,由于子类方法中的注解覆盖了父类的注解,Spring框架将不会在父类的方法中启动事务。因此,当MyRepositorysave()方法被调用时,事务将不会被启动,也不会回滚。这将导致数据不一致的问题,因为在MyRepositorysave()方法中进行的数据库操作将不会回滚。

12.嵌套事务的坑

@Service
public class TianLuoServiceInOutService {

    @Autowired
    private TianLuoFlowService tianLuoFlowService;
    @Autowired
    private TianLuoMapper tianLuoMapper;

    @Transactional
    public void addTianLuo(TianLuo tianluo) throws Exception {
        tianLuoMapper.save(tianluo);// 因tianLuoFlowService.saveFlow事务回滚,该方法事务也回滚
        tianLuoFlowService.saveFlow(tianluo);
    }
}

@Service
public class TianLuoFlowService {

    @Autowired
    private TianLuoFlowMapper tianLuoFlowMapper;

    @Transactional(propagation = Propagation.NESTED)
    public void saveFlow(TianLuo tianLuo) {
        tianLuoFlowMapper.save(tianLuo);
        throw new RuntimeException(); //继续往上抛
    }
}

以上代码使用了嵌套事务,如果saveFlow出现运行时异常,会继续往上抛,到外层addTianLuo的方法,导致tianLuoMapper.save也会回滚啦。如果不想因为被内部嵌套的事务影响,可以用try-catch包住,如下:

    @Transactional
    public void addTianLuo(TianLuo tianluo) throws Exception {
        tianLuoMapper.save(tianluo);
        try {
            tianLuoFlowService.saveFlow(tianluo);
        } catch (Exception e) {
          log.error("save tian luo flow fail,message:{}",e.getMessage());
        }
    }

13. 事务多线程调用

@Service
public class TianLuoService {

    @Autowired
    private TianLuoMapper tianLuoMapper;

    @Autowired
    private TianLuoFlowService tianLuoFlowService;

    @Transactional
    public void addTianLuo(TianLuo tianluo) {
        //保存tianluo数据库记录
        tianLuoMapper.save(tianluo);
        //多线程调用
        new Thread(() -> {
            tianLuoFlowService.saveFlow(tianluo);
        }).start();
    }
}

@Service
public class TianLuoFlowService {

    @Autowired
    private TianLuoFlowMapper tianLuoFlowMapper;

    @Transactional
    public void save(TianLuo tianLuo) {
        tianLuoFlowMapper.saveFlow(tianLuo);
    }
}

事务不生效原因:这是因为Spring事务是基于线程绑定的,每个线程都有自己的事务上下文,而多线程环境下可能会存在多个线程共享同一个事务上下文的情况,导致事务不生效。

在Spring事务管理器中,通过TransactionSynchronizationManager类来管理事务上下文。TransactionSynchronizationManager内部维护了一个ThreadLocal对象,用来存储当前线程的事务上下文。在事务开始时,TransactionSynchronizationManager会将事务上下文绑定到当前线程的ThreadLocal对象中,当事务结束时,TransactionSynchronizationManager会将事务上下文从ThreadLocal对象中移除。

14.异常被捕获并处理了,没有重新抛出

@Service
public class TianLuoServiceImpl implements TianLuoService {
    @Transactional
    public void addTianLuo(TianLuo tianluo) {
        try {
           ...
        } catch (Exception e) {
            log.error("add TianLuo error,id:{},message:{}", tianluo.getId(),e.getMessage());
        }
    }
}
  • 事务不生效的原因: 事务中的异常已经被业务代码捕获并处理,而没有被正确地传播回事务管理器,事务将无法回滚。我们可以从spring源码(TransactionAspectSupport这个类)中找到答案:

  • 解决方案:在spring事务方法中,当我们使用了try-catch,如果catch住异常,记录完异常日志什么的,一定要重新把异常抛出来,正例如下:

15.手动抛了别的异常

以上面的第10点比较类似

@Service
public class TianLuoServiceImpl implements TianLuoService {

    @Autowired
    private TianLuoMapper tianLuoMapper;
    
    @Autowired
    private TianLuoFlowMapper tianLuoFlowMapper;

    @Transactional
    public void addTianLuo(TianLuo tianluo) throws Exception {
        //保存tianluo数据库记录
        tianLuoMapper.save(tianluo);
        //保存tianluo流水数据库记录
        tianLuoFlowMapper.saveFlow(tianluo);
        throw new Exception();
    }
}

与上面的第10点比较类似

  • 失效的原因:上面的代码例子中,手动抛了Exception异常,但是是不会回滚的,因为Spring默认只处理RuntimeException和Error,对于普通的Exception不会回滚,除非,用rollbackFor属性指定配置。
  • 解决方案:添加属性配置@Transactional(rollbackFor = Exception.class)

注解为事务范围的方法中,事务的回滚仅仅对于unchecked的异常有效。对于checked异常无效。也就是说事务回滚仅仅发生在,出现RuntimeException或Error的时候。通俗一点就是:代码中出现的空指针等异常,会被回滚。而文件读写、网络超时问题等,spring就没法回滚了。

非原创,详细内容参考以下链接,这个链接是原文:

spring 事务不生效的15中场景

此内容只是为了学习,加深印象,仅作为个人学习笔记, 请支持原创, 请支持原创,内容请点击 spring 事务不生效的15中场景

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1149871.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

手机桌面待办事项APP推荐,手机上可使用哪些待办事项APP

生活中,无论你是一名专业人士,学生,还是家庭主妇,总有各种各样的任务等待着你,有时候需要额外的工具来提醒和管理这些待办事项。手机上的待办事项APP软件成为了这个任务的好帮手,为我们提供了快速、方便的方…

STM32的bootloader程序(通过串口更新STM32应用程序)

1 什么是bootloader? Bootloader,也被称为引导加载程序,是操作系统启动过程中的一个重要组成部分。它是存储在非易失性存储器中的一段小程序,负责在操作系统内核运行之前加载并启动一些必要的系统组件。 当计算机开机后&#xff0…

C++设计模式_19_Memento 备忘录(理解,目前多使用序列化方案来实现)

Memento 备忘录模式也属于“状态变化”模式,它是一个小模式,在今天来看有些过时,当今已经很少使用当前模式实现需求,思想却不变(信息隐藏),目前多使用序列化方案来实现。本系列所介绍的模式&…

资深8年测试总结,接口测试常用测试点汇总(精辟详细)

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 接口测试是测试系…

【VPX630】青翼 基于KU115 FPGA+C6678 DSP的6U VPX通用超宽带实时信号处理平台

板卡概述 VPX630是一款基于6U VPX总线架构的高速信号处理平台,该平台采用一片Xilinx的Kintex UltraScale系列FPGA(XCKU115)作为主处理器,完成复杂的数据采集、回放以及实时信号处理算法。采用一片带有ARM内核的高性能嵌入式处理器…

当面试问你接口测试时,不要再说不会了

很多人会谈论接口测试。到底什么是接口测试?如何进行接口测试?这篇文章会帮到你。 前端和后端 在谈论接口测试之前,让我们先明确前端和后端这两个概念。 前端是我们在网页或移动应用程序中看到的页面,它由 HTML 和 CSS 编写而成…

Unity DOTS系列之Struct Change核心机制分析

最近DOTS发布了正式的版本, 我们来分享一下DOTS里面Struct Change机制,方便大家上手学习掌握Unity DOTS开发。 基于ArchType与Chunk的Entity管理机制 我们回顾以下ECS的内存管理核心机制,基于ArchTypeChunk的Entity管理模式。每个Entity不直接存放数据&#xff0c…

css矩形盒子实现虚线流动边框+css实现step连接箭头

由于项目里需要手写步骤条 且实现指定状态边框虚线流动效果&#xff0c;故使用css去绘制步骤条连接箭头和绘制边框流动效果 效果&#xff1a; 1.绘制步骤条连接箭头 <ul class"process-list"><div v-for"(process, index) in processes" :key&qu…

万圣节到了尽情狂欢吧Halloween is here Have a good time!

万圣节前夜是在10月31日庆祝的一个节日&#xff0c;根据传统&#xff0c;万圣节前夜的庆祝活动从太阳落山开始。Halloween is a holiday celebrated on October 31. By tradition, Halloween begins after sunset. 为了庆祝万圣节&#xff0c;孩子们化装成女巫、鬼和恶魔。To c…

『第十四章』雨燕的自我修养:Swift 调试技巧(下)

在本篇博文中,您将学到如下内容: 6. Xcode 界面调试6.1 Xcode 预览(Preview)7. 分析编译阶段8. 强大的 Instruments 工具9. Xcode 15 新结构化日志调试10. 一些调试小技巧总结离离原上草,一岁一枯荣. 野火烧不尽,春风吹又生 6. Xcode 界面调试 我们知道 App 在 Xcode 中运行…

kafka为什么如此之快?

天下武功&#xff0c;唯快不破。同样的&#xff0c;kafka在消息队列领域&#xff0c;也是非常快的&#xff0c;这里的块指的是kafka在单位时间搬运的数据量大小&#xff0c;也就是吞吐量&#xff0c;下图是搬运网上的一个性能测试结果&#xff0c;在同步发送场景下&#xff0c;…

一百九十七、Java——IDEA项目中把多层文件夹拆开显示

一、目的 由于IDEA项目中&#xff0c;默认的是把文件夹连在一起显示&#xff0c;于是为了方便需要把这些连在一起的文件夹拆开&#xff0c;分层显示 如文件夹cn.kgc 二、解决措施 解决方法很简单 &#xff08;一&#xff09;找到IDEA项目上的小齿轮 &#xff08;二&#xf…

QT实现用本地资源管理器来打开文件夹

QString path"文件夹路径";QDesktopServices::openUrl(QUrl("file:"path, QUrl::TolerantMode)); 在windows中QT编程&#xff0c;使用资源管理器来打开指定本地文件夹的方法&#xff1a; 第一种&#xff1a;使用Qprocess命令&#xff08;相当于在cmd命令管…

c语言基础:L1-064 估值一亿的AI核心代码

以上图片来自新浪微博。 本题要求你实现一个稍微更值钱一点的 AI 英文问答程序&#xff0c;规则是&#xff1a; 无论用户说什么&#xff0c;首先把对方说的话在一行中原样打印出来&#xff1b;消除原文中多余空格&#xff1a;把相邻单词间的多个空格换成 1 个空格&#xff0c…

生产环境接口频繁502

目录 基本信息 问题现象 问题原因 排查过程 解决方案 基本信息 客户名称&#xff1a;xx集团 产品名称&#xff1a;ATS 3.0微服务单体 版本号&#xff1a;3.1.14.X 问题分类&#xff1a;环境问题 环境类型&#xff1a;K8S 问题现象 单体组织计划的新增/修改功能&#xff0…

基于STM32的多功能智能密码锁控制设计

**单片机设计介绍&#xff0c;1653基于STM32的多功能智能密码锁控制设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序文档 六、 文章目录 一 概要 基于STM32的多功能智能密码锁控制设计是一种用STM32微控制器开发的系统&#xff0c;用于控制和管理密码…

html获取网络数据,列表展示 第二种

html获取网络数据&#xff0c;列表展示 第二种 js遍历json数组中的json对象 image.png || - 判断数据是否为空&#xff0c;为空就显示 - <!DOCTYPE html> <html><head><meta charset"utf-8"><title>网页列表</title><script …

1.7 攻击面和攻击树

思维导图&#xff1a; 1.7 攻击面与攻击树 攻击面: 描述计算机和网络系统面对的安全威胁和攻击。 定义: 攻击面是由系统中可访问和可利用的漏洞所组成。常见攻击面: 向外部Web及其他服务器开放的端口和相应代码。防火墙内部的服务。处理入站数据、电子邮件、XML文件、Office文档…

css写个三角形

点击三角形&#xff0c;展开或者收起内容 <template><div><div class"zhankai" click"btn()">展开 <span :class"{sanjiao:true,rotate:flag}"></span></div><!-- 展示或者收起 --><el-collapse-…