iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >Spring事务管理中的异常回滚案例分析
  • 656
分享到

Spring事务管理中的异常回滚案例分析

2023-07-05 02:07:14 656人浏览 泡泡鱼
摘要

这篇文章主要介绍了spring事务管理中的异常回滚案例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Spring事务管理中的异常回滚案例分析文章都会有所收获,下面我们一起来看看吧。问题场景某项目系统中,se

这篇文章主要介绍了spring事务管理中的异常回滚案例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Spring事务管理中的异常回滚案例分析文章都会有所收获,下面我们一起来看看吧。

问题场景

项目系统中,serviceA 中调用的 serviceB ,并且对 serviceB 进行 tryCache

@Service("testAService")public class TestAServiceImpl implements TestAService {    @Resource    private TestAMapper testAMapper;    @Resource    private TestBService testBService;    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)    public void saveTestA(TestA entity) {        testAMapper.insertSelective(entity);        try {            testBService.saveTestB(new TestB());        } catch (Exception e) {            logger.error("调用B失败", e);        }        // 模拟做其他的数据库操作事情        testAMapper.updateSelective(entity);    }}

testBService 中模拟抛出异常:

@Service("testBService")public class TestBServiceImpl mplements TestBService {    @Resource    private TestBMapper testBMapper;    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)    @Override    public void saveTestB(TestB entity) {        testBMapper.insertSelective(entity);        throw new RuntimeException("自定义异常");    }}

问 在Controller层中调用 TestAService.saveTestA() 会怎么样?

    @Resource    private TestAService testAService;    @apiOperation(value = "Spring事务嵌套测试")    @GetMapping("springTransactionTest")    public ResponseVO<NoBody> springTransactionTest() {        testAService.saveTestA(new TestA());        return ResponseVO.success();    }

答案是:

testAService testBService 中的数据库操作 全部回滚,并且抛出的错误异常:

Transaction rolled back because it has been marked as rollback-only

原因是:

testBService.saveTestB 也增加了同样的事务注解 @Transactional

且事务隔离机制为 “REQUIRED” ,因此 两方法执行期间为同一个数据库事务,被同一个Spring事务管理器所管理着。

由于最开始开启事务者为 testAService.saveTestA,则真正执行回滚操作在 saveTestA 方法中 (Spring 规定了只有新创建的事务才会真正进行提交或回滚),

因此 saveTestB 方法中异常时只设置了当前事务状态为 RollbackOnly

org.springframework.jdbc.datasource.DataSourceTransactionManager#doSetRollbackOnly

虽然 saveTestA 中 tryCache 了 saveTestB 中的异常,企图吃掉异常信息让 saveTestA 中的事务正常提交,但是 saveTestB 里面已经设置了 当前事务状态为 RollbackOnly, 出现了冲突矛盾!

因此事务全部回滚,并且抛出异常信息:

 Transaction rolled back because it has been marked as rollback-only

Spring 管理事务的原理

首先,事务一般是关系型数据库中的概念,主要目的就是 保证一系列的增删改 sql操作 要么全部成功,要么全部回滚。

MySQL中的事务管理

Mysql中采用SQL命令进行事务管理:

  • START TRANSACTION 或 BEGIN 或 SET autocommit = 0 开启事务

  • 执行 CRUD

  • COMMIT 提交事务

  • ROLLBACK 回滚事务

这里重点说下 多条SQL在一个事务中,其中有部分SQL执行失败情况下,最终执行结果是什么

> begin;
> insert_sql1 (insert into test1(id,name)value(1,'aaa')) ;
> insert_sql2 (insert into test2(id,name)value(1,'bbb')) ;
> commit/ rollback;

上面示意代码,如果 insert_sql1 成功, insert_sql2 失败时,请问 insert_sql1 最终是否插入成功?

答案是:

首先事务不会马上回滚, 其次如果 此时执行commit则 insert_sql1 会插入成功 ,如果执行rollback则insert_sql1 会回滚。

那一般事务什么时候自动回滚或者自动提交?这里记录一下常见场景:

  • 如果事务执行中出现 DDL语句( alert create drop truncate等 ) 事务自动 commit;

  • 如果事务执行中又开启了一个事务(又出现 begin; sql命令)事务自动 commit;

  • 如果执行SQL的session 中途被关闭(SQL窗口关闭,服务器断电等) 事务自动 rollback;

JDBC中的事务管理

JDBC中连接数据库进行事务管理:

  • 获取连接 Connection con = DriverManager.getConnection()

  • con.setAutoCommit(true/false); 开启事务

  • 执行CRUD

  • con.commit() ; 提交事务

  • con.rollback(); 回滚事务

  • 关闭连接 conn.close();

JDBC 事务管理的本质还是连接了数据库执行各类数据库中开启关闭事务的SQL命令

Spring中的事务管理

Spring通过自身aop切面功能,代理各个业务方法调用 JDBC中的方法进行开启、关闭、提交、回滚事务等操作。

至于嵌套事务、各类传播机制是如何实现, 这里简单总结,虽然不能体现Spring 事务操作方面的强大,但可以很快有个大致理解。

Spring 通过 一个Map 存放了当前数据库连接对象,这是为了解决根据设定的传播机制 ( propagation ) 决定是否要新开一个事务,新开另外一个事务需要重新申请一个数据库连接。

Spring 通过 数据库事务中的 SAVEPOINT 保留点功能实现 嵌套事务的传播机制。

这里记录一下一个Http请求 从Controller层发起数据库操作请求到回滚的log日志,用于加强理解:

    @ApiOperation(value = "事务回滚测试")    @PostMapping("rollbackTest")    public ResponseVO<NoBody> rollbackTest() {        // 简单模拟插入一条记录        testAService.saveTestA(new TestA());        return ResponseVO.success();    }

[http-NIO-9902-exec-5] o.s.WEB.servlet.DispatcherServlet        : POST "/api/rollbackTest", parameters={}
[http-nio-9902-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.guzt.main.model.test.web.DbTestController#rollbackTest()
[http-nio-9902-exec-5] o.s.j.d.DataSourceTransactionManager     : Creating new transaction with name [com.sdjictec.wms.main.model.test.service.impl.TestAServiceImpl.saveTestA]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Exception
[http-nio-9902-exec-5] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@7fec11ca] for JDBC transaction
[http-nio-9902-exec-5] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@7fec11ca] to manual commit
[http-nio-9902-exec-5] c.s.w.m.m.t.d.T.insertSelective          : ==>  Preparing: INSERT INTO t_test_a ( ID,NAME ) VALUES(?,? )
[http-nio-9902-exec-5] c.s.w.m.m.t.d.T.insertSelective          : ==> Parameters: 1655001437939(String), p4xfy8(String)
[http-nio-9902-exec-5] c.s.w.m.m.t.d.T.insertSelective          : <==    Updates: 1
[http-nio-9902-exec-5] o.s.j.d.DataSourceTransactionManager     : Initiating transaction rollback
[http-nio-9902-exec-5] o.s.j.d.DataSourceTransactionManager     : Rolling back JDBC transaction on Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@7fec11ca]
[http-nio-9902-exec-5] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@7fec11ca] after transaction
[http-nio-9902-exec-5] c.s.w.m.c.p.context.CurrentUserContext   : CurrentUserContext remove CurrentUserVO...
[http-nio-9902-exec-5] c.s.w.m.f.a.CurrentUserContextAspect     : CurrentUserContextAspect doAfterThrowing  CurrentUserContext.remove()...
[http-nio-9902-exec-5] .m.m.a.ExceptionHandlerExceptionResolver : Using @ExceptionHandler com.guzt.main.framework.exception.GlobalExceptionHandler#handleBusinessException(BusinessException)
[http-nio-9902-exec-5] c.s.w.m.f.e.GlobalExceptionHandler       : BusinessException -- errorCode:E5111 errORMsg:模拟数据库操作异常,主键重复
[http-nio-9902-exec-5] m.m.a.RequestResponseBodyMethodProcessor : Using 'application/JSON', given [*/*] and supported [application/json, application/json, application/*+json, application/json, application/*+json, application/cbor]
[http-nio-9902-exec-5] m.m.a.RequestResponseBodyMethodProcessor : Writing [ResponseVO(code=-1, message=模拟数据库操作异常,主键重复, data={"errorBody":"","bussinessCode":"E5111","extraMsg":""})]
[http-nio-9902-exec-5] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [com.guzt.starter.common.exception.BusinessException: errorCode=E5111, errorMsg=FAIL]
[http-nio-9902-exec-5] o.s.web.servlet.DispatcherServlet        : Completed 200 OK

主要的步骤:

Creating new transaction

DataSourceTransactionManager 根据 TransactionDefinition 创建 TransactionStatus 对象,准备开启事务Acquired Connection for JDBC transaction

通过数据库连接池 申领一个数据库连接Switching JDBC Connection to manual commit

开启事务,底层是通过JDBC执行 SET autocommit = 0; Initiating transaction rollback

准备回滚事务,修改 TransactionStatus 状态为回滚Rolling back JDBC transaction

回滚事务 ,底层是通过JDBC执行 rollback; SET autocommit = 1;Releasing JDBC Connection

释放数据库连接,最后是关闭了数据库连接,底层调用了 JDBC Connection.close()

Spring中的事务接口

上面日志中提到了 TransactionDefinition TransactionStatus 等接口,这里也顺便总结一下Spring事务中重要的几个接口。

接口含义说明
PlatformTransactionManager事务管理器各类数据库操作框架自行实现该接口,例如 DataSourceTransactionManager(JDBC), JtaTransactionManager, HibernateTransactionManager
TransactionDefinition事务定义信息(隔离级别、传播行为、超时、只读、回滚规则) ,
一般在@Transactional 中指定这些属性
如果是注解方式的事务,Spring 通过 AnnotationTransactionAttributeSource.getTransactionAttribute(Method method, Class<?> targetClass) 创建,参数中的method就是开启事务的业务方法
TransactionStatus事务运行状态,包含是否已完成,是否只读,是否有恢复点,是否只回滚等事务管理器接口 PlatformTransactionManager 通过 getTransaction(TransactionDefinition definition) 方法来得到一个事务

至于具体功能流程,这里不讨论,自行看源码,多debug即可明白。

到底回滚还是不回滚

本次讨论重点就是 Spring的事务管理中,遇到了程序异常到底会不会回滚?

简明答案

执行事务的方法如果感知到了异常则将回滚事务

什么是执行事务的方法

一般方法上增加了 @Transactional 注解或 其他Spring事务支持的方式AOP代理了的方法

  @Transactional   public void saveTestA(TestA entity) {        业务代码   }

什么情况下异常被感知

被Spring事务AOP所代理的业务方法执行时出现异常,且异常类在Spring事务回滚范围内的将被调用方法所感知。

默认Spring只对 unchecked Exception 进行回滚,一般手动设定全部异常(rollbackFor = Exception.class)

什么情况下异常不被感知

一般这是讨论事务不生效的场景。

  • 方法访问修饰符非public

  • 法抛出的异常不是spring的事务支持的异常

  • 被 Try-Cache 捕获且不再向外抛出例如下面的场景代码:

   @Transactional   public void saveTestA(TestA entity) {     try{        业务代码     } catch (Exception e) {         logger.error("内部消化异常,不往外抛", e);     }   }

注解所在的类没有被Spring 事务AOP代理

这种场景问题最隐蔽,一般需要有经验或者多次debug才能发现

同一个类里面方法互相调用,一般建议采用 SpringUtil.getBean(this.getClass()).xxxx事务方法()

某些策略模式场景,需要将service对象放到一个 Map<String, Service>中 ,如果是自行放置,则对象必须是 代理对象而非 this对象,建议采用 SpringUtil.getBean(this.getClass()) 对象。

参考代码:

public class WxinOrderTypeServiceImpl implements OrderTypeService {  @PostConstruct  public void init() {      ORDER_TYPE_SERVICE.put("orderTypeKey", SpringUtil.getBean(this.getClass());  }  @Transactional(rollbackFor = Exception.class)  @Override  public void saveOrder(Order order)    logger.info("微信订单");  }}

上面代码意思是,将Spring代理的对象 WxinOrderTypeServiceImpl$Cjlibxxxx 放置到策略map ORDER_TYPE_SERVICE中去,如果用 this ( ORDER_TYPE_SERVICE.put("orderTypeKey",this)) 则事务不生效,因为this虽然也在Spring beanFactory中但没有被事务AOP所代理,因此用this 会不生效。

备注: SpringUtil 其实就是实现了接口 ApplicationContextAware 获得ApplicationContext,ApplicationContext 中有 getBean方法,当然SpringBoot 可以在业务类里面直接注入 ApplicationContext

@AutowiredApplicationContext applicationContext;

数据库本身不支持事务

如果使用mysql且存储引擎是MyISAM,则事务是不起作用的

异常被感知后Spring做些什么

异常被感知后,Spring将会做回滚或更新TransactionStatus的状态(doSetRollbackOnly).

一旦TransactionStatus 被打上了 RollbackOnly标志后,那么不管中间的业务代码是什么 都会抛出异常进行全部事务回滚。

那么什么时候 不做回滚只更新 TransactionStatus 为 RollbackOnly?

参见文章一开始的业务代码,同属于一个事务中(Propagation.REQUIRED)的多个执行事务方法(业务代码中嵌套调用其他service方法),如果不是首次开启事务的那个方法则都只会更新 TransactionStatus 为 RollbackOnly,事务的提交回滚由首次事务开启的那个方法执行

回滚程度是多少

事务回滚到哪一个程度,是全部嵌套调用的方法都回滚还是部分方法回滚,这里主要是由Spring的事务传播机制功能控制。

分类行为说明回滚程度
加入当前事务PROPAGATION_REQUIRED默认方式,如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务全部回滚
加入当前事务PROPAGATION_SUPPORTS如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行全部回滚
加入当前事务PROPAGATION_MANDATORY如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常全部回滚
不加入当前事务PROPAGATION_REQUIRES_NEW创建一个新的事务,则把当前事务挂起Java里面还是同一个线程,只新创建了另外一个数据库连接开启事务,如果新事务回滚且程序异常被当前事务方法感知,则当前事务方法也同样回滚
不加入当前事务PROPAGATION_NOT_SUPPORTED以非事务方式运行,如果当前存在事务,则把当前事务挂起Java里面还是同一个线程 如果程序异常被当前事务方法感知,则当前事务方法也同样回滚
不加入当前事务PROPAGATION_NEVER以非事务方式运行,如果当前存在事务,则抛出异常抛出异常,全部回滚
嵌套当前事务PROPAGATION_NESTED如果当前存在事务,则创建一个事务嵌套在当前事务中运行;如果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED只回滚自己  底层采用数据库SavePoint功能

关于“Spring事务管理中的异常回滚案例分析”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Spring事务管理中的异常回滚案例分析”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网精选频道。

--结束END--

本文标题: Spring事务管理中的异常回滚案例分析

本文链接: https://www.lsjlt.com/news/349155.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Spring事务管理中的异常回滚案例分析
    这篇文章主要介绍了Spring事务管理中的异常回滚案例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Spring事务管理中的异常回滚案例分析文章都会有所收获,下面我们一起来看看吧。问题场景某项目系统中,se...
    99+
    2023-07-05
  • Spring事务管理中的异常回滚是什么
    目录前言问题场景Spring 管理事务的原理MySQL中的事务管理JDBC中的事务管理Spring中的事务管理Spring中的事务接口到底回滚还是不回滚简明答案什么是执行事务的方法什...
    99+
    2023-02-09
    Spring事务管理异常回滚 Spring异常回滚 Spring事务管理中的异常回滚
  • spring如何实现事务异常回滚
    这篇文章将为大家详细讲解有关spring如何实现事务异常回滚,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。为了打印清楚日志,很多方法我都加tyrcatch,在catch中打印日志。但是这边情况来了,当这个...
    99+
    2023-05-30
  • Spring事务回滚异常怎么解决
    当Spring事务回滚异常时,可以尝试以下解决方法: 检查代码逻辑:首先检查代码逻辑是否正确,是否在事务范围内执行了可能导致异常...
    99+
    2024-03-01
    Spring
  • Spring事务捕获异常后依旧回滚的解决
    目录前沿问题阐述知识点前置条件问题追踪总结前沿 一段生产事故发人深省,在Spring的声明式事务中手动捕获异常,居然判定回滚了,这是什么操作?话不多说直接上代码 @Service p...
    99+
    2024-04-02
  • Spring框架中异常处理情况的示例分析
    这篇文章主要为大家展示了“Spring框架中异常处理情况的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Spring框架中异常处理情况的示例分析”这篇文章吧。1.编写一个类,实现Hand...
    99+
    2023-06-20
  • React16中异常处理的示例分析
    这篇文章主要介绍React16中异常处理的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!异常处理在 React 15.x 及之前的版本中,组件内的异常有可能会影响到 Reac...
    99+
    2024-04-02
  • Netty分布式pipeline管道异常传播事件的示例分析
    这篇文章主要介绍了Netty分布式pipeline管道异常传播事件的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。传播异常事件简单的异常处理的场景@Overridep...
    99+
    2023-06-29
  • Python中的异常处理实例分析
    一、什么是异常在python中,错误触发的异常如下二、异常的种类在python中不同的异常可以用不同的类型去标识,一个异常标识一种错误。1 、常用异常类AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有...
    99+
    2023-05-16
    Python
  • Java中异常处理的示例分析
    这篇文章主要介绍Java中异常处理的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!异常的定义在java中,异常就是java在编译、运行或运行过程中出现的错误总共有三种:1.编译错误 2.运行错误 3.逻辑错误...
    99+
    2023-06-25
  • 关于Spring中@Transactional事务回滚的注意事项
    目录一、Spring 默认事务1.1、抛出 unchecked 和 checked 异常都回滚1.2、总结二、使用 Spring中 @Transactional 注解的注意事项一、S...
    99+
    2023-05-19
    Spring @Transactional Spring 事务回滚
  • SpringBoot中异常处理实例分析
    这篇文章主要介绍“SpringBoot中异常处理实例分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“SpringBoot中异常处理实例分析”文章能帮助大家解决问题。一、背景在我们编写程序的过程中,...
    99+
    2023-06-30
  • Spring事务开启原理的示例分析
    这篇文章给大家分享的是有关Spring事务开启原理的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。在事务配置类上声明@EnableTransactionManagement注解开启事务在事务配置类上定义数...
    99+
    2023-06-14
  • JavaScript中事件与异常捕获的示例分析
    小编给大家分享一下JavaScript中事件与异常捕获的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!事件处理【onCl...
    99+
    2024-04-02
  • Python中的Selenium异常处理实例分析
    本篇内容介绍了“Python中的Selenium异常处理实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!在进行爬虫爬取淘宝商品信息时候...
    99+
    2023-06-30
  • 关于SpringBoot的异常回滚和事务的使用详解
    目录Springboot中事务的使用:开启事务的方法中事务回滚的情况:Springboot @Transactional 事务不回滚一、异常捕获的原因二、数据库引擎不支持回滚(使用M...
    99+
    2023-05-19
    SpringBoot异常回滚 SpringBoot事务
  • C#中类的异常处理实例分析
    今天小编给大家分享一下C#中类的异常处理实例分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。前言日常编码过程中,最重要的技...
    99+
    2023-06-29
  • 深入浅析Java中的Spring事务管理
    深入浅析Java中的Spring事务管理?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。环境与版本本文出来之前的一篇文章中的hibernate的相关lib 外Java事务管理之H...
    99+
    2023-05-31
    java spring ava
  • Spring Boot全局统一异常处理器的示例分析
    这篇文章主要介绍Spring Boot全局统一异常处理器的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!一、封装统一返回结果类import com.jiusen.exercise.enums.Err...
    99+
    2023-06-15
  • c++中异常的示例分析
    这篇文章主要介绍了c++中异常的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一、什么是异常处理一句话:异常处理就是处理程序中的错误。二、为什么需要异常处理,异常处理...
    99+
    2023-06-15
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作