Spring中事务失效的场景

news2024/11/14 4:05:17

文章目录

  • 1 抛出检查异常导致事务不能正确回滚
    • 1.1 异常演示
    • 1.2 解决办法
  • 2 业务方法内自己 try-catch 异常导致事务不能正常回滚
    • 1.1 异常演示
    • 1.2 解决办法
  • 3 aop切面顺序导致事务不能正确回滚
    • 3.1 异常演示
    • 3.2 解决办法
  • 4 非 public 方法导致事务的失效
    • 4.1 异常演示
    • 4.2 解决办法
  • 5 父子容器导致的事务失效
    • 5.1 异常演示
    • 5.2 解决办法
  • 6 调用本类方法导致传播行为失效
    • 6.1 异常演示
    • 6.2 解决办法
  • 7 @Transactional 没有保证原子行为
    • 7.1 异常演示
    • 7.2 解决办法
  • 8 @Transactional 方法导致的 synchronized 失效

Spring中事务失效大致有以下八种场景

导入数据源

drop table if exists account;
create table account
(
    accountNo int primary key auto_increment,
    balance   int not null
);

insert into account (accountNo, balance)
values (1, 1000),
       (2, 1000);

1 抛出检查异常导致事务不能正确回滚

并不是任何异常情况下,spring都会回滚事务,默认情况下,RuntimeException和Error的情况下,spring事务才会回滚。

原因:

  • Spring默认只会回滚非检查异常(运行时异常和Error)

解法:

  • 配置rollbackFor属性

1.1 异常演示

 @Transactional
 public void transfer(int from, int to, int amount) throws FileNotFoundException {
      int fromBalance = accountMapper.findBalanceBy(from);
      if (fromBalance - amount >= 0) {
          accountMapper.update(from, -1 * amount);
          //异常
          new FileInputStream("aaa");
          accountMapper.update(to, amount);
      }
  }

在这里插入图片描述
可见事务并未回滚

1.2 解决办法

在这里插入图片描述

 @Transactional(rollbackFor = Exception.class)
 public void transfer(int from, int to, int amount) throws FileNotFoundException {
      int fromBalance = accountMapper.findBalanceBy(from);
      if (fromBalance - amount >= 0) {
          accountMapper.update(from, -1 * amount);
          //异常
          new FileInputStream("aaa");
          accountMapper.update(to, amount);
      }
  }

在这里插入图片描述
在这里插入图片描述

可见事务是进行了回滚的,数据库的数据也并未改变

2 业务方法内自己 try-catch 异常导致事务不能正常回滚

原因:

  • 事务通知只有捕捉到了目标异常抛出的异常,才能进行后续的回滚处理,如果目标自己处理掉异常,事务通知无法知悉

解法1:异常原样抛出

解法2:手动设置 TransactionInterceptor.currentTransactionStatus().setRollbackOnly();

1.1 异常演示

 @Transactional(rollbackFor = Exception.class)
public void transfer(int from, int to, int amount)  {
      try {
          int fromBalance = accountMapper.findBalanceBy(from);
          if (fromBalance - amount >= 0) {
              accountMapper.update(from, -1 * amount);
              new FileInputStream("aaa");
              accountMapper.update(to, amount);
          }
      } catch (FileNotFoundException e) {
          e.printStackTrace();
      }
  }

在这里插入图片描述
可见事务还是进行了提交,并未回滚

1.2 解决办法

@Transactional(rollbackFor = Exception.class)
public void transfer(int from, int to, int amount)  {
     try {
         int fromBalance = accountMapper.findBalanceBy(from);
         if (fromBalance - amount >= 0) {
             accountMapper.update(from, -1 * amount);
             new FileInputStream("aaa");
             accountMapper.update(to, amount);
         }
     } catch (FileNotFoundException e) {
         e.printStackTrace();
//            TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
         throw new RuntimeException(e);
     }
 }

抛出异常或者手动设置事务状态进行回滚

在这里插入图片描述

3 aop切面顺序导致事务不能正确回滚

原因:事务切面优先级最低,但如果自定义的切面优先级和他一样,则还是自定义切面在内层,这时若自定义切面没有正确抛出异常,事务就无法回滚

解法1:异常原样抛出

解法2:手动设置 TransactionInterceptor.currentTransactionStatus().setRollbackOnly();

3.1 异常演示

在这里插入图片描述

 @Aspect
 static class MyAspect {
     @Around("execution(* transfer(..))")
     public Object around(ProceedingJoinPoint pjp) throws Throwable {
         LoggerUtils.get().debug("log:{}", pjp.getTarget());
         try {
             return pjp.proceed();
         } catch (Throwable e) {
             e.printStackTrace();
             return null;
         }
     }
 }

3.2 解决办法

抛出异常或者手动设置事务状态进行回滚
在这里插入图片描述

4 非 public 方法导致事务的失效

原因:

  • Spring为方法创建代理、添加事务通知、前提条件都是该方法是 pubic 的

解决办法:

  • 改为public方法

4.1 异常演示

 @Transactional
 void transfer(int from, int to, int amount) throws FileNotFoundException {
      int fromBalance = accountMapper.findBalanceBy(from);
      if (fromBalance - amount >= 0) {
          accountMapper.update(from, -1 * amount);
          accountMapper.update(to, amount);
      }
  }

在这里插入图片描述
无事务的开启和提交

4.2 解决办法

@Transactional
public void transfer(int from, int to, int amount) throws FileNotFoundException {
     int fromBalance = accountMapper.findBalanceBy(from);
     if (fromBalance - amount >= 0) {
         accountMapper.update(from, -1 * amount);
         accountMapper.update(to, amount);
     }
 }

在这里插入图片描述

5 父子容器导致的事务失效

原因:

  • 子容器扫描范围过大,把未加事务的配置的 service 扫描进来

解法:

  • 各扫各的,不要图简便
  • 不要用父子容器,所有bean放在同一容器(SpringBoot项目就只有一个容器,不会发生该情况

5.1 异常演示

以下是两个配置类:

@ComponentScan("day04.tx.app")
public class WebConfig {
}
@Configuration
@PropertySource("classpath:jdbc.properties")
@EnableTransactionManagement
@EnableAspectJAutoProxy(exposeProxy = true)
@ComponentScan("day04.tx.app.service")
@MapperScan("day04.tx.app.mapper")
public class AppConfig {

  @ConfigurationProperties("jdbc")
   @Bean
   public DataSource dataSource() {
       return new HikariDataSource();
   }

   @Bean
   public DataSourceInitializer dataSourceInitializer(DataSource dataSource, DatabasePopulator populator) {
       DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
       dataSourceInitializer.setDataSource(dataSource);
       dataSourceInitializer.setDatabasePopulator(populator);
       return dataSourceInitializer;
   }

   @Bean
   public DatabasePopulator databasePopulator() {
       return new ResourceDatabasePopulator(new ClassPathResource("account.sql"));
   }

   @Bean
   public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {
       SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
       factoryBean.setDataSource(dataSource);
       return factoryBean;
   }

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

}

然后我们加载的时候将两个配置都加载

 public static void main(String[] args) throws FileNotFoundException {
   	 GenericApplicationContext parent = new GenericApplicationContext();
     AnnotationConfigUtils.registerAnnotationConfigProcessors(parent.getDefaultListableBeanFactory());
     ConfigurationPropertiesBindingPostProcessor.register(parent.getDefaultListableBeanFactory());
     parent.registerBean(AppConfig.class);
     parent.refresh();

     GenericApplicationContext child = new GenericApplicationContext();
     AnnotationConfigUtils.registerAnnotationConfigProcessors(child.getDefaultListableBeanFactory());
     child.setParent(parent);
     child.registerBean(WebConfig.class);
     child.refresh();

     AccountController bean = child.getBean(AccountController.class);
     bean.transfer(1, 2, 500);
 }

在这里插入图片描述

5.2 解决办法

放在一个容器里面或者各扫各的

6 调用本类方法导致传播行为失效

原因:

  • 本类方法调用不经过代理,因此无法增强

解法:

  • 依赖注入自己(代理)来调用
  • 通过AopContext拿到代理对象,来调用

6.1 异常演示

@Service
public class Service6 {
    

@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
 public void foo() throws FileNotFoundException {
     LoggerUtils.get().debug("foo");
     System.out.println(this.getClass());
     bar();
 }

 @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
 public void bar() throws FileNotFoundException {
     LoggerUtils.get().debug("bar");
 }
}

在这里插入图片描述
由运行结果结果可知:

事务只开启了一次,并且打印的this的类型不为代理类

6.2 解决办法

总的来说就是要获取代理类来调用被事务增强的方法才可以保证事务不失效

@Service
public class Service6 {

    @Autowired
    private Service6 proxy;

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void foo() throws FileNotFoundException {
        LoggerUtils.get().debug("foo");
        System.out.println(proxy.getClass());
//        System.out.println(this.getClass());
        proxy.bar();
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public void bar() throws FileNotFoundException {
        LoggerUtils.get().debug("bar");
    }
}

在这里插入图片描述

由运行结果可知,事务开启了两次,并且打印的也是代理对象

另一种方法同理,只不过要在配置时加上@EnableAspectJAutoProxy(exposeProxy = true),暴露对象才可以获取

((Service6) AopContext.currentProxy()).bar();

7 @Transactional 没有保证原子行为

原因:

  • 事务的原子性仅覆盖[insert、update、delete、select] ... for update 语句,select方法并不阻塞

7.1 异常演示

CountDownLatch latch = new CountDownLatch(2);
new MyThread(() -> {
         bean.transfer(1, 2, 1000);
     latch.countDown();
 }, "t1", "boldMagenta").start();

 new MyThread(() -> {
         bean.transfer(1, 2, 1000);
     latch.countDown();
 }, "t2", "boldBlue").start();

 latch.await();
 System.out.println(bean.findBalance(1));

在这里插入图片描述
可知数据库的数据已经出现了问题

7.2 解决办法

  • synchronized 范围应扩大至代理方法调用
    在这里插入图片描述

  • 使用 select ... for update 替换 select

select balance from account where accountNo=#{accountNo} for update

8 @Transactional 方法导致的 synchronized 失效

原因:

  • synchronized保证的仅是目标方法的原子性,环绕目标方法的还有 commit 等操作,他们并不处于 sync 块内

解法:

  • synchronized 范围应扩大至代理方法调用
  • 使用 select ... for update 替换 select

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

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

相关文章

深度学习-全卷积神经网络(FCN)

1. 简介 全卷积神经网络(Fully Convolutional Networks,FCN)是Jonathan Long等人于2015年在Fully Convolutional Networks for Semantic Segmentation一文中提出的用于图像语义分割的一种框架,是深度学习用于语义分割领域的开山之…

【目标检测】【DDPM】DiffusionDet:用于检测的概率扩散模型

文章目录摘要一、Introduction二、相关工作三、方法1.准备工作2.架构3、训练4.预测过程四. 实验1.训练策略2.main property3.消融实验五、代码分析1.测试 demo.py2.训练 train-net.py总结摘要 我们提出了扩散det,一个新的框架,将目标检测作为一个从噪声…

OpenGL基础程序结构

用OpenGL编写的程序结构类似于用其他语言编写的程序。实际上,OpenGL是一个丰富的三维图形函数库,编写OpenGL程序并非难事,只需在基本C语言中调用这些函数,用法同Turbo C、Microsoft C等类似,但也有许多不同之处。   …

[附源码]Python计算机毕业设计Django校园招聘系统

项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等等。 环境需要 1.运行环境:最好是python3.7.7,…

功能测试求职难,现在不懂自动化测试连外包都进不去了?

功能测试求职难 最近因为公司政策原因,部分外包被裁员,其中不乏能力还不错,工作也挺踏实的,比较可惜,为了帮助他们尽快找到下家,我这边也开始帮他们关注招聘情况,发现一个挺让我意外的事情。在…

Java中CAS详解

一、什么是CAS 什么是CAS机制 CAS机制是一种数据更新的方式。在具体讲什么是CAS机制之前,我们先来聊下在多线程环境下,对共享变量进行数据更新的两种模式:悲观锁模式和乐观锁模式。 悲观锁更新的方式认为:在更新数据的时候大概率…

Android 动画和过渡

Android 动画和过渡 (一) 动画 JetpackCompose提供了强大且可扩展的API,可以轻松地在应用程序的UI中实现各种动画。下面描述了如何使用这些API以及根据动画场景使用哪些API。 动画在现代移动应用程序中至关重要,以实现流畅和可理解…

【语音处理】基于自适应差分脉冲编码调制(ADPCM)的实现研究(Matlab代码实现)

👨‍🎓个人主页:研学社的博客 💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜…

目标检测算法——安全帽识别数据集(附下载链接)

🎄🎄近期,小海带在空闲之余收集整理了一批安全帽识别数据集供大家参考。 整理不易,小伙伴们记得一键三连喔!!!🎈🎈 目录 一、安全帽佩戴数据集 二、SHWD安全帽佩戴检测…

五、分页总结

文章目录一、分页的几种方式1、使用关键字实现分页(limit)2、通过 RowBounds 类实现分页查询3、使用分页插件实现分页分页的作用:在进行查询时如果数据量庞大的话会造成大量的内存压力,让程序卡顿。这时候使用分页减少数据的处理量…

基于SpringBoot编写starter,自己写个类库引入使用

我们在做springboot项目的时候经常会创建项目后先引入各种starter,比如操作数据库的mybatis-plus-boot-starter,数据源的druid-spring-boot-starter等等。引入starter的好处就是可以将第三方的依赖库快速的整合到我们自己的springboot项目中,…

​最新Xcode9 无证书真机调试流程

写在前面 公司分配了新的测试机,证书99台名额已满,所以上网找教程,学习了一下如何使用Xcode无证书进行真机调试。​ 一. 创建证书​ 1. 运行Xcode, Xcode–》Preference–》添加账号(能在appstore下载的账号)​ 2. 选中刚才添加的AppleID–…

JSP 社区联动系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 社区联动系统 是一套完善的web设计系统,对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开 发,数据库为Mysql,使用ja…

鸿蒙harmony天气预报Demo

1.准备工作 1.1创建项目 sdk为6版本,所以使用华为的远程模拟器p40即可。 1.2准备图片资源 这里把天气预报用到的天气提示的图片全放在资源目录下的media文件下。 具体资源在github仓库已包含,自行前往。 1.3配置文件 接着是修改配置文件&#xff0…

[附源码]Python计算机毕业设计SSM家教管理系统(程序+LW)

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

DevComponents.DotNetBar2之SuperTabControl使用技巧

关于类似SuperTabControl的使用如何动态调整其TAB标签的顺序问题,搜了全网也没有找到类似答案,都提到tab键的顺序或者是通过控件界面进行调整其顺序,都不是想要的结果,有个网友问的类似问题但是没有一个答案可用。经过反复测试总结…

华为云Nginx配置

配置yum源 mkdir -p /etc/yum.repos.d/repo_bak/ mv /etc/yum.repos.d/*.repo /etc/yum.repos.d/repo_bak/ cd /etc/yum.repos.d wget http://mirrors.myhuaweicloud.com/repo/mirrors_source.sh && sh mirrors_source.sh清除原有yum缓存 yum clean all 执行生成新的…

Win10编译Android版本的FFmpeg库

安装MSYS2 下载地址:MSYS2 安装完成后打开MSYS2执行如下命令, 安装所需要的工具链 pacman -S --needed base-devel mingw-w64-x86_64-toolchain下载android NDK 我在Android Studio里面已经下载过了,没有的可以自己百度去官网下载NDK 我…

[矩阵论] Unit 4. 矩阵的广义逆 - 知识点整理

注: 以下内容均由个人整理, 不保证完全准确, 如有纰漏, 欢迎交流讨论参考: 杨明, 刘先忠. 矩阵论(第二版)[M]. 武汉: 华中科技大学出版社, 2005 4 矩阵的广义逆 4.1 矩阵的左逆与右逆 左逆 右逆 Def’ 4.1: 设 A∈CmnA\in C^{m\times n}A∈Cmn ∃B∈Cnm\exists B\in C^{n\t…

MySQL数据库的性能优化及自动化运维与Mysql高并发优化详细教程

首先,我们来看看DBA的具体工作,我觉得 DBA 真的很忙:备份和恢复、监控状态、集群搭建与扩容、数据迁移和高可用,这是我们 DBA 的功能。 了解这些功能以后要对体系结构有更加深入的了解,你不知道怎么处理这些故障和投诉…