前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >java事务回滚案例_java事务控制

java事务回滚案例_java事务控制

作者头像
全栈程序员站长
发布于 2022-09-27 03:10:16
发布于 2022-09-27 03:10:16
1.7K00
代码可运行
举报
运行总次数:0
代码可运行

大家好,又见面了,我是你们的朋友全栈君。

疑问,确实像往常一样在service上添加了注解 @Transactional,为什么查询数据库时还是发现有数据不一致的情况,想想肯定是事务没起作用,出现异常的时候数据没有回滚。于是就对相关代码进行了一番测试,结果发现一下踩进了两个坑,确实是事务未回滚导致的数据不一致。

下面总结一下经验教训:

Spring事务的管理操作方法

编程式的事务管理

实际应用中很少使用

通过使用TransactionTemplate 手动管理事务

声明式的事务管理

开发中推荐使用(代码侵入最少)

Spring的声明式事务是通过AOP实现的

主要掌握声明式的事务管理。

spring事务不回滚的两个原因

总结一下导致事务不回滚的两个原因,一是Service类内部方法调用,二是try…catch异常。

1. Service类内部方法调用

大概就是 Service 中有一个方法 A,会内部调用方法 B, 方法 A 没有事务管理,方法 B 采用了声明式事务,通过在方法上声明 Transactional 的注解来做事务管理。示例代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service
public class RabbitServiceImpl implements RabbitService {

  @Autowired
  private RabbitDao rabbitDao;
  @Autowired
  private TortoiseDao tortoiseDao;

  @Override
  public Rabbit methodA(String name){
    return methodB(name);
  }

  @Transactional(propagation = Propagation.REQUIRED)
  public boolean methodB(String name){
    rabbitDao.insertRabbit(name);
    tortoiseDao.insertTortoise(name);
    return true;
  }

}

单元测试代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class RabbitServiceImplTest {

  @Autowired
  private RabbitService rabbitService;

  // 事务未开启
  @Test
  public void testA(){
    rabbitService.methodA("rabbit");
  }

  // 事务开启
  @Test
  public void testB(){
    rabbitService.methodB("rabbit");
  }
}

从上一节中可以看到,声明式事务是通通过AOP动态代理实现的,这样会产生一个代理类来做事务管理,而目标类(service)本身是不能感知代理类的存在的。

对于加了@Transactional注解的方法来说,在调用代理类的方法时,会先通过拦截器TransactionInterceptor开启事务,然后在调用目标类的方法,最后在调用结束后,TransactionInterceptor 会提交或回滚事务,大致流程如下图:

总结,在方法 A 中调用方法 B,实际上是通过“this”的引用,也就是直接调用了目标类的方法,而非通过 Spring 上下文获得的代理类,所以事务是不会开启的。

2. try…catch异常

在一段业务逻辑中对数据库异常进行了处理,使用了try…catch子句捕获异常并throw了一个自定义异常,这种情况导致了事务未回滚,示例代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Transactional(propagation = Propagation.REQUIRED)
public boolean methodB(String name) throws BizException {
  try {
    rabbitDao.insertRabbit(name);
    tortoiseDao.insertTortoise(name);
  } catch (Exception e) {
    throw new BizException(ReturnCode.EXCEPTION.code, ReturnCode.EXCEPTION.msg);
  }
  return true;
}

BizException的定义如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class BizException extends Exception {
  // 自定义异常
}

上面代码中的声明式事务在出现异常的时候,事务是不会回滚的。在代码中我虽然捕获了异常,但是同时我也抛出了异常,为什么事务未回滚呢?猜测是异常类型不对,于是开始查询原因,翻看了Spring的官方文档,找到了答案。下面是翻译自Spring官网。

17.5.3 声明式事务的回滚

上一节中介绍了如何设置开启Spring事务,一般在你的应用的Service层代码中设置,这一节将介绍在简单流行的声明式事务中如何控制事务回滚。

在Spring FrameWork 的事务框架中推荐的事务回滚方法是,在当前执行的事务上下文中抛出一个异常。如果异常未被处理,当抛出异常调用堆栈的时候,Spring FrameWork 的事务框架代码将捕获任何未处理的异常,然后并决定是否将此事务标记为回滚。

在默认配置中,Spring FrameWork 的事务框架代码只会将出现runtime, unchecked 异常的事务标记为回滚;也就是说事务中抛出的异常时RuntimeException或者是其子类,这样事务才会回滚(默认情况下Error也会导致事务回滚)。在默认配置的情况下,所有的 checked 异常都不会引起事务回滚。

注:Unchecked Exception包括Error与RuntimeException. RuntimeException的所有子类也都属于此类。另一类就是checked Exception。

你可以精确的配置异常类型,指定此异常类事务回滚,包括 checked 异常。下面的xml代码片段展示了如何配置checked异常引起事务回滚,应用自定义异常类型:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<tx:advice id="txAdvice" transaction-manager="txManager">
 <tx:attributes>
 <tx:method name="get*" read-only="true" rollback-for="Exception"/>
 <tx:method name="*"/>
 </tx:attributes>
</tx:advice>

与其有同等作用的注解形式如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Transactional(rollbackForClassName={"Exception"})
或者
@Transactional(rollbackFor={Exception.class})

在你遇到异常不想回滚事务的时候,同样的你也可指定不回滚的规则,下面的一个例子告诉你,即使遇到未处理的 InstrumentNotFoundException 异常时,Spring FrameWork 的事务框架同样会提交事务,而不回滚。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<tx:advice id="txAdvice">
 <tx:attributes>
 <tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
 <tx:method name="*"/>
 </tx:attributes>
</tx:advice>

与其有同样作用的注解形式如下:   

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Transactional(noRollbackForClassName={"InstrumentNotFoundException"})
或者
@Transactional(noRollbackFor={InstrumentNotFoundException.class})

还有更灵活的回滚规则配置方法,同时指定什么异常回滚,什么异常不回滚。当Spring FrameWork 的事务框架捕获到一个异常的时候,会去匹配配置的回滚规则来决定是否标记回滚事务,使用匹配度最强的规则结果。因此,下面的配置例子表达的意思是,除了异常 InstrumentNotFoundException 之外的任何异常都会导致事务回滚。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<tx:advice id="txAdvice">
 <tx:attributes>
 <tx:method name="*" rollback-for="Throwable" no-rollback-for="InstrumentNotFoundException"/>
 </tx:attributes>
</tx:advice>

你也可以通过编程式的方式回滚一个事务,尽管方法非常简单,但是也有非常强的代码侵入性,使你的业务代码和Spring FrameWork 的事务框架代码紧密的绑定在一起,示例代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void resolvePosition() {
 try {
   // some business logic...
 } catch (NoProductInStockException ex) {
   // trigger rollback programmatically
   TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
 }
}

如果可能的话,强烈推荐您使用声明式事务方式回滚事务,对于编程式事务,如果你强烈需要它,也是可以使用的,but its usage flies in the face of achieving a clean POJO-based architecture.(没懂…)

看完官方文档这节内容找到了问题的答案,原来是因为我们自定义的异常不是 RuntimeException。我的解决办法是,在注解@Transactional中添加 rollbackFor={BizException.class}。可能你会问我为什么不将自定义异常修改为继承RuntimeException,因为我需要BizException是一个checked 异常。

以上这篇完美解决Spring声明式事务不回滚的问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持脚本之家。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/179167.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
每日一博 - 常见的Spring事务失效&事务不回滚案例集锦
在事务方法add中,直接调用事务方法updateStatus。 updateStatus方法拥有事务的能力是因为spring aop生成代理了对象,但是这种方法直接调用了this对象的方法,所以updateStatus方法不会生成事务。
小小工匠
2021/09/08
1.5K0
深入理解Spring框架中的声明式事务管理
事务管理是数据库操作中的关键环节,确保数据的一致性和完整性。在复杂的业务逻辑中,事务管理能保证操作的原子性,即要么全部成功,要么全部失败。
windealli
2024/06/25
3580
深入理解Spring框架中的声明式事务管理
Spring的编程式事务和声明式事务
事务管理对于企业应用来说是至关重要的,当出现异常情况时,它也可以保证数据的一致性。 Spring事务管理的两种方式 spring支持编程式事务管理和声明式事务管理两种方式。 编程式事务使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。 声明式事务是建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行
nnngu
2018/04/04
1.8K0
Spring 事务失效的六种情况
数据库事务是指作为单个逻辑工作单元执行的一系列操作,这些操作要么一起成功,要么一起失败,是一个不可分割的工作单元。
Jensen_97
2023/07/20
5540
Spring 事务失效的六种情况
spring事务回滚的多种方式「建议收藏」
转:https://www.cnblogs.com/zeng1994/p/8257763.html
全栈程序员站长
2022/11/03
2.2K0
Spring 声明式事务常用的二种配置方式
Spring 声明式事务常用的二种配置方式         声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。         显然声明式事务管理要优于编程式事务管理,这正是spr
秋日芒草
2018/05/15
9580
Spring事务的那些坑,这里都给你总结好了!
Spring框架已是JAVA项目的标配,其中Spring事务管理也是最常用的一个功能,但如果不了解其实现原理,使用姿势不对,一不小心就可能掉坑里。
程序员白楠楠
2020/11/22
1.6K0
Spring事务(下)
用来解决并发事务时出现的问题,其使用TransactionDefinition中的静态变量来指定
码农戏码
2021/03/23
8580
Spring学习笔记 事务管理
所谓事务,指的是程序中可运行的不可分割的最小单位。在生活中事务也是随处可见的。比方说你在Steam上剁手买了一款游戏,那么付款就是一个事务,要么付款成功,游戏到手;要么付款失败,钱退回你账户。不可能也绝不应该出现花了钱游戏却没到的情况。所以,事务也应该具有两个操作:成功时候提交,或者失败时候回滚。
乐百川
2022/05/05
3890
全面分析 Spring 的编程式事务管理及声明式事务管理
开始之前 关于本教程 本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务。通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之。 先决条件 本教程假定您已经掌握了 Java 基础知识,并对 Spring 有一定了解。您还需要具备基本的事务管理的知识,比如:事务的定义,隔离级别的概念,等等。本文将直接使用这些概念而不做详细解释。另外,您最好掌握数据库的基础知识,虽然这不是必须。 系统需求 要试验这份教程中的工具和示例,硬件配置需求为:至少带有 512M
java达人
2018/01/31
7280
Spring的事务控制
第一:JavaEE 体系进行分层开发,事务处理位于业务层,Spring 提供了分层设计业务层的事务处理解决方案。 第二:spring 框架为我们提供了一组事务控制的接口。具体在后面的第二小节介绍。这组接口是在spring-tx-5.0.2.RELEASE.jar 中。 第三:spring 的事务控制都是基于 AOP 的,它既可以使用编程的方式实现,也可以使用配置的方式实现。我们学习的重点是使用配置的方式实现。
暴躁的程序猿
2022/03/23
3410
Spring5 事务
service注入dao,在dao注入JdbcTemplate,在JdbcTemplate注入DataSource
用户9615083
2022/12/25
3370
Spring5 事务
Spring5之事务
(1)事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操 作都失败
yuanshuai
2022/08/22
2190
Spring事务回滚和异常类
转载自 https://www.cnblogs.com/tianyuchen/p/6678084.html
allsmallpig
2021/02/25
1.1K0
8-Spring事务控制
事务是一个比较广泛的概念,事务管理资源除了我们熟知的数据库外,还可以包含消息队列、文件系统等。当然,一般来说,我们说的事务单指“数据库事务”。
Ywrby
2022/10/27
3150
Spring5–06—事务操作:(Spring事务管理介绍)
(1)@Transactional,这个注解添加到类上面,也可以添加方法上面 (2)如果把这个注解添加类上面,这个类里面所有的方法都添加事务 (3)如果把这个注解添加方法上面,为这个方法添加事务
Java架构师必看
2021/05/14
5400
Spring5–06—事务操作:(Spring事务管理介绍)
Spring5.0源码学习系列之事务管理概述
Spring5.0源码学习系列之事务管理概述(十一),在学习事务管理的源码之前,需要对事务的基本理论比较熟悉,所以本章节会对事务管理的基本理论进行描述
烂猪皮
2021/07/16
3180
Spring5.0源码学习系列之事务管理概述
Spring框架:第八章:声明式事务
10、声明式事务 事务分为声明式和编程式两种: 声明式事务:声明式事务是指通过注解的形式对事务的各种特性进行控制和管理。 编码式(编程式)事务:指的是通过编码的方式实现事务的声明。
Java廖志伟
2022/09/28
3560
Spring框架:第八章:声明式事务
基于可靠消息方案的分布式事务(二):Java中的事务
前言:在上一篇文章 基于可靠消息方案的分布式事务:Lottor介绍 中介绍了常见的分布式事务的解决方案以及笔者基于可靠消息方案实现的分布式事务组件Lottor的原理,并展示了应用的控制台管理。在正式介绍Lottor的具体实现之前,本文首先将会介绍Java中的事务管理,重点介绍Spring的事务管理。PS:有很多读者提问Lottor是否开源,这里统一回答:是开源的,Lottor目前在笔者所在公司的内部项目应用,并且笔者在将耦合的业务代码重构,将会在下一篇文章同步更新到GitHub,敬请期待。本文较长,适合电脑
aoho求索
2018/06/04
1.2K0
spring中@transactional注解的作用(spring 事务实现原理)
事物管理对于企业应用来说是至关重要的,好使出现异常情况,它也可以保证数据的一致性。
全栈程序员站长
2022/07/30
8270
相关推荐
每日一博 - 常见的Spring事务失效&事务不回滚案例集锦
更多 >
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验