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


这篇文章主要介绍了Spring事务管理中的异常回滚案例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Spring事务管理中的异常回滚案例分析文章都会有所收获,下面我们一起来看看吧。某项目系统中,serviceA 中调用的 serviceB ,并且对 serviceB 进行 tryCachetestBService 中模拟抛出异常:问 在Controller层中调用 TestAService.saveTestA() 会怎么样?答案是:testAService testBService 中的数据库操作 全部回滚,并且抛出的错误异常:Transaction rolled back because it has been marked as rollback-only原因是:testBService.saveTestB 也增加了同样的事务注解 @Transactional且事务隔离机制为 “REQUIRED” ,因此 两方法执行期间为同一个数据库事务,被同一个Spring事务管理器所管理着。由于最开始开启事务者为 testAService.saveTestA,则真正执行回滚操作在 saveTestA 方法中 (Spring 规定了只有新创建的事务才会真正进行提交或回滚),因此 saveTestB 方法中异常时只设置了当前事务状态为 RollbackOnlyorg.springframework.jdbc.datasource.DataSourceTransactionManager#doSetRollbackOnly虽然 saveTestA 中 tryCache 了 saveTestB 中的异常,企图吃掉异常信息让 saveTestA 中的事务正常提交,但是 saveTestB 里面已经设置了 当前事务状态为 RollbackOnly, 出现了冲突矛盾!因此事务全部回滚,并且抛出异常信息:Transaction rolled back because it has been marked as rollback-only首先,事务一般是关系型数据库中的概念,主要目的就是 保证一系列的增删改 SQL操作 要么全部成功,要么全部回滚。在MySQL中采用SQL命令进行事务管理:START TRANSACTION 或 BEGIN 或 SET autocommit = 0 开启事务执行 CRUDCOMMIT 提交事务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中连接数据库进行事务管理:获取连接 Connection con = DriverManager.getConnection()con.setAutoCommit(true/false); 开启事务执行CRUDcon.commit() ; 提交事务con.rollback(); 回滚事务关闭连接 conn.close();JDBC 事务管理的本质还是连接了数据库执行各类数据库中开启关闭事务的SQL命令Spring通过自身AOP切面功能,代理各个业务方法调用 JDBC中的方法进行开启、关闭、提交、回滚事务等操作。至于嵌套事务、各类传播机制是如何实现, 这里简单总结,虽然不能体现Spring 事务操作方面的强大,但可以很快有个大致理解。Spring 通过 一个Map 存放了当前数据库连接对象,这是为了解决根据设定的传播机制 ( propagation ) 决定是否要新开一个事务,新开另外一个事务需要重新申请一个数据库连接。Spring 通过 数据库事务中的 SAVEPOINT 保留点功能实现 嵌套事务的传播机制。这里记录一下一个HTTP请求 从Controller层发起数据库操作请求到回滚的log日志,用于加强理解:[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 : [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 transactionDataSourceTransactionManager 根据 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()上面日志中提到了 TransactionDefinition TransactionStatus 等接口,这里也顺便总结一下Spring事务中重要的几个接口。至于具体功能流程,这里不讨论,自行看源码,多debug即可明白。本次讨论重点就是 Spring的事务管理中,遇到了程序异常到底会不会回滚?执行事务的方法如果感知到了异常则将回滚事务一般方法上增加了 @Transactional 注解或 其他Spring事务支持的方式AOP代理了的方法被Spring事务AOP所代理的业务方法执行时出现异常,且异常类在Spring事务回滚范围内的将被调用方法所感知。默认Spring只对 unchecked Exception 进行回滚,一般手动设定全部异常(rollbackFor = Exception.class)一般这是讨论事务不生效的场景。方法访问修饰符非public法抛出的异常不是spring的事务支持的异常被 Try-Cache 捕获且不再向外抛出例如下面的场景代码:注解所在的类没有被Spring 事务AOP代理这种场景问题最隐蔽,一般需要有经验或者多次debug才能发现同一个类里面方法互相调用,一般建议采用 SpringUtil.getBean(this.getClass()).xxxx事务方法()某些策略模式场景,需要将service对象放到一个 Map中 ,如果是自行放置,则对象必须是 代理对象而非 this对象,建议采用 SpringUtil.getBean(this.getClass()) 对象。参考代码:上面代码意思是,将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数据库本身不支持事务如果使用MySQL且存储引擎是MyISAM,则事务是不起作用的异常被感知后,Spring将会做回滚或更新TransactionStatus的状态(doSetRollbackOnly).一旦TransactionStatus 被打上了 RollbackOnly标志后,那么不管中间的业务代码是什么 都会抛出异常进行全部事务回滚。那么什么时候 不做回滚只更新 TransactionStatus 为 RollbackOnly?参见文章一开始的业务代码,同属于一个事务中(Propagation.REQUIRED)的多个执行事务方法(业务代码中嵌套调用其他service方法),如果不是首次开启事务的那个方法则都只会更新 TransactionStatus 为 RollbackOnly,事务的提交回滚由首次事务开启的那个方法执行事务回滚到哪一个程度,是全部嵌套调用的方法都回滚还是部分方法回滚,这里主要是由Spring的事务传播机制功能控制。关于“Spring事务管理中的异常回滚案例分析”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Spring事务管理中的异常回滚案例分析”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注百云主机行业资讯频道。

相关推荐: Element el-upload上传组件怎么使用

今天小编给大家分享一下Elementel-upload上传组件怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。upload上传是前端开发很…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

Like (0)
Donate 微信扫一扫 微信扫一扫
Previous 05/10 18:46
Next 05/10 18:46

相关推荐