Spring事务失效场景

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

一、数据库引擎不支持事务

以 MySQL为例,MyISAM引擎是不支持事务操作的,一般要支持事务都会使用InnoDB引擎
从MySQL 5.5.5 开始的默认存储引擎是 InnoDB,之前默认的都是 MyISAM,所以这一点要值得注意,如果底层引擎不支持事务,那么再怎么设置也没有用。
在这里插入图片描述

二、没有被 Spring 管理

此时把@Service注解注释掉,那么这个类就不会被加载成一个Bean,这个类就不会Spring管理了,事务自然就失效了。

public class OrderServiceImpl implements OrderService{

  @Transactional
  public void updateOrder(Order order){
    //update order
  }
}

因为Spring事务是由AOP机制实现的,也就是说从Spring IOC容器获取bean时,Spring会为目标类创建代理,来支持事务的。但是@Service被注释后,service类都不是spring管理的

三、方法不是 public 的或者被final 、static关键字修饰

@Transactional注解只能用于public 的方法上,否则事多不会生效,如果要用在非public的方法上,则可以开启基于 AspcetJ 框架的静态代理模式。
Spring事务是由AOP机制实现的,AOP机制的本质就是动态代理,而代理的事务方法不是public的话,computeTransactionAttribute()就会返回null,也就是这时事务属性不存在了。

@Service
public class TianLuoServiceImpl implements TianLuoService {

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

	//spring事务方法addTianLuo的访问权限不是public
    @Transactional
    private void addTianLuo(TianLuo tianluo) {
        tianLuoMapper.save(tianluo);
        tianLuoFlowMapper.saveFlow(buildFlowByTianLuo(tianluo));
    }
}

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

@Service
public class MyServiceImpl{

	@Autowired
	private MyMapper myMapper;

	@Autowired
	private MyFlowMapper myFlowMapper;

	//如果一个方法被声明为final或者static,则该方法不能被子类重写,
	//也就是说无法在该方法上进行动态代理,这会导致Spring无法生成事务代理对象来管理事务。
	@Transactional
	public final void addSomething(Things things){
		
		myMapper.save(things);
		myFlowMapper.saveFlow(buildFlowByMy(things));
	}
}

四、发生自身调用

事务是通过Spring AOP代理来实现的,而在同一个类中,一个方法调用另一个方法时,调用方法直接调用目标方法的代码,而不是通过代理类进行调用。

@Service
public class TianLuoServiceImpl implements TianLuoService {

    @Autowired
    private TianLuoMapper tianLuoMapper;
    
    @Autowired
    private TianLuoFlowMapper tianLuoFlowMapper;
    
    public void addTianLuo(TianLuo tianluo){
     // 调用内部的事务方法
     //调用目标executeAddTianLuo方法不是通过代理类进行的,因此事务不生效
     this.executeAddTianLuo(tianluo);
   }

    @Transactional
    public void executeAddTianLuo(TianLuo tianluo) {
        tianLuoMapper.save(tianluo);
        tianLuoFlowMapper.saveFlow(buildFlowByTianLuo(tianluo));
    }
}

解决方案:可以新建多一个类,让这两个方法分开,分别在不同的类中。如下:

@Service
public class TianLuoExecuteServiceImpl implements TianLuoExecuteService {

    @Autowired
    private TianLuoMapper tianLuoMapper;
    @Autowired
    private TianLuoFlowMapper tianLuoFlowMapper;
    
    @Transactional
    public void executeAddTianLuo(TianLuo tianluo) {
        tianLuoMapper.save(tianluo);
        tianLuoFlowMapper.saveFlow(buildFlowByTianLuo(tianluo));
    }
}

@Service
public class TianLuoAddServiceImpl implements TianLuoAddService {

    @Autowired
    private TianLuoExecuteService tianLuoExecuteService;
    
    public void addTianLuo(User user){
     tianLuoExecuteService.executeAddTianLuo(user);
   }
}

有时候也可以在该 Service 类中注入自己,或者通过AopContext.currentProxy()获取代理对象。

五、没有配置事务管理器

如果没有配置以下DataSourceTransactionManager数据源事务管理器,那么事务也不会生效 :

@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
  return new DataSourceTransactionManager(dataSource);
}  

但在 Spring Boot 中只要引入了 spring-boot-starter-data-jdbc 启动器依赖就会自动配置DataSourceTransactionManager数据源事务管理器,所以 Spring Boot框架不存在这个问题,但在传统的 Spring 框架中需要注意。

解决方案:(如果是Spring Boot项目,它默认会自动配置事务管理器并开启事务支持。)

@Configuration
public class AppConfig {
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }
}

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

六、设置了不支持事务

@Service
public class OrderServiceImpl implements OrderService {
  @Transactional
  public void update(Order order) {
    updateOrder(order);
  }
  
  @Transactional(propagation = Propagation.NOT_SUPPORTED)
  public void updateOrder(Order order) {
    //update order
  }
}

这里的Propagation.NOT_SUPPORTED表示当前方法不以事务方式运行,当前若存在事务则挂起,这就是主动不支持以事务方式运行了

七、异常没有被抛出

这个方法把异常给捕获了,但没有抛出来,所以事务不会回滚,只有捕捉到异常事务才会生效。

@Service
public class TianLuoServiceImpl implements TianLuoService {

    @Autowired
    private TianLuoMapper tianLuoMapper;

    @Autowired
    private TianLuoFlowMapper tianLuoFlowMapper;

    @Transactional
    public void addTianLuo(TianLuo tianluo) {
        try {
            //保存tianluo数据库记录
            tianLuoMapper.save(tianluo);
            //保存tianluo flow数据库记录
            tianLuoFlowMapper.saveFlow(tianluo);
        } catch (Exception e) {
            log.error("add TianLuo error,id:{},message:{}", tianluo.getId(),e.getMessage());
        }
    }

}

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

@Service
public class TianLuoServiceImpl implements TianLuoService {

    @Autowired
    private TianLuoMapper tianLuoMapper;

    @Autowired
    private TianLuoFlowMapper tianLuoFlowMapper;

    @Transactional(rollbackFor = Exception.class)
    public void addTianLuo(TianLuo tianluo) {
        try {
            tianLuoMapper.save(tianluo);
            tianLuoFlowMapper.saveFlow(tianluo);
        } catch (Exception e) {
            log.error("add TianLuo error,id:{},message:{}", tianluo.getId(),e.getMessage());
            throw e;
        }
    }
}

手动抛了别的异常
Spring默认只处理RuntimeException和Error,对于普通的Exception不会回滚,除非,用rollbackFor属性指定配置
解决方案:添加属性配置@Transactional(rollbackFor = Exception.class)

@Service
public class TianLuoServiceImpl implements TianLuoService {

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

    @Transactional
    public void addTianLuo(TianLuo tianluo) throws Exception {
        
        tianLuoMapper.save(tianluo);
        
        tianLuoFlowMapper.saveFlow(tianluo);
        throw new Exception(); //手动抛了Exception异常,但是是不会回滚的
    }
}

八、异常类型不匹配

其实rollbackFor属性指定的异常必须是Throwable或者其子类。默认情况下,RuntimeException和Error两种异常都是会自动回滚的。但是因为以上的代码例子,指定了rollbackFor = Error.class,但是抛出的异常又是Exception,而Exception和Error没有任何什么继承关系,因此事务就不生效。

@Service
public class TianLuoServiceImpl implements TianLuoService {

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

	//其实rollbackFor属性指定的异常
    @Transactional(rollbackFor = Error.class)
    public void addTianLuo(TianLuo tianluo) {
       
        tianLuoMapper.save(tianluo);
        
        tianLuoFlowMapper.saveFlow(tianluo);
        
        //模拟异常抛出
        throw new Exception();
    }
}

在这里插入图片描述

九、配置错误的 @Transactional 注解

虽然使用了@Transactional注解,但是注解中的readOnly=true属性指示这是一个只读事务,因此在更新User实体时会抛出异常。

@Transactional(readOnly = true)
public void updateUser(User user){
	userDao.updateUser(user);
}

解决方案:将readOnly属性设置为false,或者移除了@Transactional注解中的readOnly属性。

十、事务超时时间设置过短

timeout属性被设置为1秒,这意味着如果事务在1 秒内无法完成,则报事务超时了。

@Transactional(timeout = 1)
public void doSomething(){

}

十一、使用了错误的事务传播机制

@Service
public class TianLouServiceImpl{
	
	@Autowired
	private TianLuoMapper tianLuoMapper;
	@Autowired
	private TianLuoFlowMapper tianLuoFlowMapper;

	//Propagation.NOT_SUPPORTED传播特性不支持事务
	@Transactional(propagation = Propagation.NOT_SUPPPRTED)
	public void doInsertIianluo(TianLuo tianluo)throws Exception{
		tianLuoMapper.save(tianluo);
        tianLuoFlowMapper.saveFlow(buildFlowByTianLuo(tianluo));
	}
}

选择正确的事务传播机制:Spring提供了七种事务传播机制

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

十二、事务注解被覆盖导致事务失效

这将导致数据不一致的问题,因为在MyRepository的save()方法中进行的数据库操作将不会回滚。

public interface MyRepository {
    @Transactional
    void save(String data);
}

public class MyRepositoryImpl implements MyRepository {
    @Override
    public void save(String data) {
        // 数据库操作
    }
}

public class MyService {

    @Autowired
    private MyRepository myRepository;

	//由于子类方法中的注解覆盖了父类的注解,Spring框架将不会在父类的方法中启动事务。
    @Transactional
    public void doSomething(String data) {
        myRepository.save(data);
    }
}

public class MyTianluoService extends MyService {

	//传播行为(REQUIRES_NEW),子类方法中的注解覆盖了父类的注解
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void doSomething(String data) {
        super.doSomething(data);
    }
}

十三、嵌套事务

@Service
public class TianLuoServiceInOutService {

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

	//导致tianLuoMapper.save也会回滚啦。
    @Transactional
    public void addTianLuo(TianLuo tianluo) throws Exception {
        tianLuoMapper.save(tianluo);
        tianLuoFlowService.saveFlow(tianluo);
    }
}

@Service
public class TianLuoFlowService {

    @Autowired
    private TianLuoFlowMapper tianLuoFlowMapper;

	//如果saveFlow出现运行时异常,会继续往上抛,到外层addTianLuo的方法
    @Transactional(propagation = Propagation.NESTED)
    public void saveFlow(TianLuo tianLuo) {
        tianLuoFlowMapper.save(tianLuo);
        throw new RuntimeException();
    }
}

解决方案: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());
        }
    }

十四、事务多线程调用

Spring事务是基于线程绑定的,每个线程都有自己的事务上下文,而多线程环境下可能会存在多个线程共享同一个事务上下文的情况,导致事务不生效。
Spring事务管理器通过使用线程本地变量(ThreadLocal)来实现线程安全。

@Service
public class TianLuoService {

    @Autowired
    private TianLuoMapper tianLuoMapper;

    @Autowired
    private TianLuoFlowService tianLuoFlowService;

    @Transactional
    public void addTianLuo(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事务管理器中,通过TransactionSynchronizationManager类来管理事务上下文。TransactionSynchronizationManager内部维护了一个ThreadLocal对象,用来存储当前线程的事务上下文。在事务开始时,TransactionSynchronizationManager会将事务上下文绑定到当前线程的ThreadLocal对象中,当事务结束时,TransactionSynchronizationManager会将事务上下文从ThreadLocal对象中移除。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024-2-22 作业

作业要求: 复习前面知识点(指针、结构体、函数)整理思维导图顺序表(按位置插入、按位置删除和去重、重新写)理解链表的代码,尝试写一下链表的尾插和输出 1.复习前面知识点(指针、结构体、函数) 2.整理思维导图 3.顺序表(按位置插入、按位置删除和去重、…

172基于matlab的MPPT智能算法

基于matlab的MPPT智能算法,通过细菌觅食进行优化。算法引入了趋向性操作,用以进行局部范围内的最优寻找;引入了复制操作,用以避免种群更新盲目随机性,加快了算法的收敛速度;引入了迁徙操作用以避免算法陷入…

Linux进一步研究权限-----------ACL使用

一、使用情况 1.1、场景: 某个大公司,在一个部门,有一个经理和手下有两个员工,在操控一个Linux项目,项目又分为三期做,然而一期比较重要,经理带着员工做完了,公司就觉得技术难点已经做完攻克了&#xff0…

视频评论抓取软件|抖音数据抓取工具

最近我们推出了一款基于C#语言开发的工具。这款工具提供了丰富的功能,旨在帮助用户轻松获取抖音视频内容。让我们一起来详细介绍一下这款工具的主要功能模块: 1. 批量视频提取: 工具提供了便捷的批量视频提取功能,用户只需输入关…

Vue学习之计算属性

模板中的表达式虽然方便,但也只能用来做简单的操作。如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。比如说,我们有这样一个包含嵌套数组的对象: const author reactive({name: John Doe,books: [Vue 2 - Advan…

Windows环境下使用SSH的开源图形化SFTP工具客户端 简介和基本使用

在Windows环境下,有许多开源的图形化SFTP工具客户端可以使用,其中比较受欢迎的是WinSCP和FileZilla。下面我将分别介绍这两个工具的基本信息和使用方法。 WinSCP WinSCP是一个Windows环境下使用的开源图形化SFTP客户端,它也支…

06 Qt自绘组件:Switch动画开关组件

系列文章目录 01 Qt自定义风格控件的基本原则-CSDN博客 02 从QLabel聊起:自定义控件扩展-图片控件-CSDN博客 03 从QLabel聊起:自定义控件扩展-文本控件-CSDN博客 04 自定义Button组件:令人抓狂的QToolButton文本图标居中问题-CSDN博客 0…

第一个 Angular 项目 - 添加服务

第一个 Angular 项目 - 添加服务 这里主要用到的内容就是 [Angular 基础] - service 服务 提到的 前置项目在 第一个 Angular 项目 - 动态页面 这里查看 想要实现的功能是简化 shopping-list 和 recipe 之间的跨组件交流 回顾一下项目的结构: ❯ tree src/app/…

Linux---权限管理(ACL权限、特殊位和隐藏属性)

目录 1.ACT权限 1.1什么是ACT权限 1.2ACT图解 2.操作步骤 2.1添加测试目录、用户、组,并将用户添加到组 2.2修改目录的所有者和所属组 2.3设定权限 2.4为临时用户分配权限 2.4.1添加临时用户 2.4.2为临时用户分配特定权限 2.4.3查看目录权限,注…

【C语言】详解计算机二级c语言程序题

文章目录 前言资料相关程序题 一(字符串)程序题 二(数组)程序题 三(基础)程序题 四(结构体)程序题 五(结构体)程序题 六(基础) 前言 …

无人机精准定位技术,GPS差分技术基础,RTK原理技术详解

差分GPS的基本原理 差分GPS(Differential GPS,简称DGPS)的基本原理是利用一个或多个已知精确坐标的基准站,与用户(移动站)同时接收相同的GPS卫星信号。由于GPS定位时会受到诸如卫星星历误差、卫星钟差、大…

springboot+vue项目部署配置开机自启动

1.前端部属 下载nginx解压,在nginx\conf下找到nginx.conf 添加如下代码 server {listen 8081;server_name localhost;charset utf-8;location / {root F:/1ceshi/dist; #前端打包路径try_files $uri $uri/ /index.html;index index.html index.htm;}l…

123 Linux C++ 系统编程2 Linux 上安装卸载程序三种方法,linux 下解压缩命令 tar介绍。kill命令,top命令,umask 命令

一 通过命令和网络直接安装 sudo apt-get update sudo apt-get update 的工作就是将自己本地 ubutun的软件列表和 aliyun 的软件列表对比,如不一样,则更新。 sudo apt-get install 软件名 真正的安装 那么这里就有一个问题了, 怎么从aliy…

操作系统(1)——学习导论(Ⅰ)

目录 小程一言专栏链接: [link](http://t.csdnimg.cn/6grrU) 学习导论什么是操作系统主要功能强调 操作系统历史硬件层面处理器重要特点and功能 存储器磁盘I/O设备小程常用的I/O设备及其特点 小程一言 本操作系统专栏,是小程在学操作系统的过程中的第一步&#xff…

STL初始---C++

STL目录 1.STL的产生原因2.STL基本概念与六大组件2.1基本概念2.2六大组件 3.STL中容器、算法、迭代器3.1容器3.2算法3.3迭代器 4.STL简单应用4.1vector存放内置数据类型4.2vector存放自定义数据类型4.3vector容器嵌套容器 1.STL的产生原因 长久以来,软件界一直希望…

使用代理IP技术实现爬虫同步获取和保存

概述 在网络爬虫中,使用代理IP技术可以有效地提高爬取数据的效率和稳定性。本文将介绍如何在爬虫中同步获取和保存数据,并结合代理IP技术,以提高爬取效率。 正文 代理IP技术是一种常用的网络爬虫技术,通过代理服务器转发请求&a…

MDS300-16-ASEMI电源控制柜MDS300-16

编辑:ll MDS300-16-ASEMI电源控制柜MDS300-16 型号:MDS300-16 品牌:ASEMI 封装:M25 最大重复峰值反向电压:1600V 最大正向平均整流电流(Vdss):300A 功率(Pd):大功率 芯片个数&#xff1…

记录 使用FFMPEG 笔记本摄像头推流

一、使用 FFMPEG 测试摄像头拉流显示 # 获取摄像头名称 ffmpeg -list_devices true -f dshow -i dummy# 我笔记本上的摄像头名称如下 device_pnp_\\?\usb#vid_0408&pid_1020&mi_00#6&199e90f7&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global# 使…

基于JAVA的房屋出售出租系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 房屋销售模块2.2 房屋出租模块2.3 预定意向模块2.4 交易订单模块 三、系统展示四、核心代码4.1 查询房屋求租单4.2 查询卖家的房屋求购单4.3 出租意向预定4.4 出租单支付4.5 查询买家房屋销售交易单 五、免责说明 一、摘…

vue手写卡片切换,并且点击获取到卡片信息

需求:做一个卡片样式的列表,之后有一些基本信息,之后卡片选中后样式不一样,默认选中第一个卡片,点击卡片后可以获取到卡片的信息 一、效果 二、关键代码 index默认重0开始,activeTable默认为0,0-0等于0,但…