为什么catch了异常,但事务还是回滚了?
前几天我发了这篇文章《我来出个题:这个事务会不会回滚?》(https://blog.didispace.com/will-this-transcation-rollback/)
得到了很多不错的反馈,也有不少读者通过微信、群或者邮件的方式,给了我一些关于test4的回复。其中还有直接发给我测试案例,来证明我的答案是错的。
今天,我们就来一起看看test4这个争议很大的问题。如果您是刚打开这篇文章,不了解我们在讨论啥,那可以先点击查看之前的这篇《我来出个题:这个事务会不会回滚?》(https://blog.didispace.com/will-this-transcation-rollback/)。
通过这两篇文章的解析,相信你会对Spring Data JPA下的事务执行机制有质的飞跃。
为什么没回滚
先来说说,那些写了代码验证"不会回滚"的情况,把这些错误答案的原因先说清楚,然后再细说test4会回滚的情况。
根据这两天读者给我的案例或者描述清楚的一些情况,归结了一下,大家写的验证代码之所以不会回滚,主要有以下三个原因:
没有按照我题目开头说的,采用InnoDB存储引擎,用了MyISAM,不支持事务,自然不会复现。
没用按照我题目开头说的,采用JPA和JSR 303校验注解,比如:用了MyBaits,所以自然也不会复现。
定义事务的函数不是public类型,这个基础用法就不对了,事务本身就没生效
归家一下出现这些疑问的原因:没审题和事务基础掌握不牢导致。关于事务基础使用的一些常见注意点,之前写过一篇文章,如果觉得这方面知识还不扎实的,建议读一读:《为什么加了@Transactional注解,事务没有回滚?》(https://blog.didispace.com/transactional-not-rollback/)
为什么写了catch,还会回滚
先来看看执行时候报的异常:
javax.validation.ConstraintViolationException: Validation failed for classes [com.didispace.chapter310.User] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[ConstraintViolationImpl{interpolatedMessage='个数必须在0和5之间', propertyPath=name, rootBeanClass=class com.didispace.chapter310.User, messageTemplate='{javax.validation.constraints.Size.message}'}
]at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:140) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:80) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.action.internal.EntityInsertAction.preInsert(EntityInsertAction.java:209) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:83) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:478) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:356) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1454) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:511) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3283) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2479) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:473) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:178) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$300(JdbcResourceLocalTransactionCoordinatorImpl.java:39) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:271) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:98) ~[hibernate-core-5.3.7.Final.jar:5.3.7.Final]
这个异常是这个回滚的关键。这个异常javax.validation.ConstraintViolationException
是哪里的呢?还记得以前说的JSR 303不?对的,是Bean Validation中的异常。
有的读者说这个不是RuntimeException
,所以不会回滚。很显然,这类判断的都没有实际尝试一下,只要点开源码可以马上发现,这个异常就是属于RunTimeException
的。
实际上,之所以会回滚,与这里使用Spring Data JPA以及Hibernate Validator有直接关系。从JPA 2.0开始,就默认支持了这些Bean Validation的实现,它提供了实体生命周期中pre-persist
, pre-update
,pre-remove
三个事件发生时来执行校验的功能。而在校验的时候,当校验失败,抛出javax.validation.ConstraintViolationException
时,当前事务就会被标记为rollback
。
源码解析
要想了解,这其中到底发生了什么,跟踪源码是最好的方式。那么源码从哪里开始看呢?从异常日志中找线索吧。
从异常栈中找到最近的一个错误,点开看看。
错误行数在532行tx.commit()
,习惯性的加上断点,这样下一次进来的时候可以看看当前情况下的各种参数情况。
同时看到下面还有个catch,既然532行出错了,那这里肯定会进,所以也加个端点,到时候可以进去看看。
执行程序,调用一下test4,执行到532行,然后进入下一步,看看会到哪里?
这个时候,会进入到org.hibernate.engine.transaction.internal.TransactionImpl
,具体位置如下:
还是习惯性的,在下面两行重要位置加上断点,以便下次可以快速到这里。
继续按上看的步骤尝试下去,可以来到下图的位置:
可以看到校验异常是从271行出来的,结合278行和280行,是不是清楚这里回滚的原因了呢?
小结
当我把上一篇问题推到很多地方之后,其实还是收到了不少负面的反馈,甚至还有说我误导读者,顺便问候了下我的祖宗。这些我就不跟爱喷的读者互杠了,我是一直都推崇碰到问题,尽量多深挖一些的学习方式。虽然有的时候出现问题,确实是由于不恰当的写法导致,但如果你没有理解这个错误的原因,本质还是对其底层逻辑不够了解。如果你能从问题中找到线索,并顺藤摸瓜地探究和思考,你能够收获到的东西,远比喷我来的强。
实践出真知,当你觉得困惑的时候,不如动手写一写,调一调,很多答案就能自然浮现!如果对于test4会回滚还不够理解的读者,那就跟着我上面的步骤,一步步尝试一下,可以观察的更深入一些,你对这部分逻辑的理解就更全面了。
我们正在组建高质量的Spring技术交流群,最近这个分享的源头也是来源于群里的一次讨论。欢迎各种热爱技术的开发者加入参与讨论。这里的每个人都有自己的闪光点,互相学习,取长补短,长期坚持,愿大家都会成为自己领域里的佼佼者!
往期推荐
骚操作!阿里云直接买www.huaweicloud.com的关键词来抢生意?
Dubbo 3.0.0正式发布:应用级服务注册,跨语言的RPC协议、更好支持Kubernetes!
我来出个题:这些事务会不会回滚?大概率你会错!
警惕 Spring Boot Actuator 引发的安全问题
来活儿了!赶紧检查下代码里有没有脏话...
关注我回复「加群」,加入Spring技术交流群
原创不易,如果您觉得本文不错
欢迎转发到朋友圈,支持一下我的努力吧!
为什么catch了异常,但事务还是回滚了?相关推荐
- sqlsever回滚操作_sqlserver事务与回滚
如果要在Production执行数据改动必须小心,可以使用事务提前验证一下自己写的SQL是不是你期望的.尤其是Update的where 条件有问题的话,跟新的记录就会超出预期的范围.如下面的语句,一着 ...
- SpringBoot 异常回滚 事务的使用___Springboot @Transactional 事务不回滚
Springboot中事务的使用: 1.启动类加上@EnableTransactionManagement注解,开启事务支持(其实默认是开启的). 2.在使用事务的public(只有public支持事 ...
- 异常被 ”吃“ 掉导致事务无法回滚
我们在处理异常时,有两种方式,要么抛出去,让上一层来捕获处理:要么把异常 try catch 掉,在异常出现的地方给处理掉.就因为有这中 try-catch,所以导致异常被 "吃" ...
- 【java基础】子线程任务发生异常,主线程事务如何回滚?
文章目录 一.提出问题 二.主线程与子线程 三.线程池 四.异常的捕获 五.事务的回滚 一.提出问题 最近有一位朋友问了我这样一个问题,问题的截图如下: 这个问题问的相对比较笼统,我来稍微详细的描述下 ...
- spring事务——try{...}catch{...}中事务不回滚的几种处理方式
当希望在某个方法中添加事务时,我们常常在方法头上添加@Transactional注解 @ResponseBody@RequestMapping(value = "/payment" ...
- java事务中使用try catch 导致事务不回滚的问题
@Transactional注解的触发,只回滚RuntimeException和Error异常,默认不回滚非RuntimeException异常 解决方法: 1.方法前添加注解(基础的 @Trans ...
- mysql事务管理及spring声明式事务中主动异常抛出使数据库回滚
mysql事务管理及spring声明式事务中主动异常抛出使数据库回滚 参考文章: (1)mysql事务管理及spring声明式事务中主动异常抛出使数据库回滚 (2)https://www.cnblog ...
- spring@Transactional注解事务不回滚不起作用无效的问题处理
这几天在项目里面发现我使用@Transactional注解事务之后,抛了异常居然不回滚.后来终于找到了原因. 如果你也出现了这种情况,可以从下面开始排查. 一.特性 先来了解一下@Transactio ...
- springboot 事务手动回滚_Spring Boot中的事务是如何实现的
1. 概述 一直在用SpringBoot中的@Transactional来做事务管理,但是很少想过SpringBoot是如何实现事务管理的,今天从源码入手,看看@Transactional是如何实现事 ...
最新文章
- jquery-datatables 销毁重新渲染
- 中国大陆开源镜像站汇总
- 机器学习(K-means聚类原理以及用法)
- ubuntu12.04samba服务器配置
- Visual Studio调试技巧
- mysql函数大全最小,MySQL函数一览_MySQL函数全部汇总
- bcc校验位怎么算的_数据BCC校验码计算工具
- 《逻辑与计算机设计基础(原书第5版)》——1.4 算术运算
- 蒜头君的生日(日期格式)
- android怎么实现记住密码功能,Android实现用户登录记住密码功能
- 员工工号怎么编码_员工编号管理制度
- 壳浏览器 android,QQ浏览器2020安卓版
- PHP语言25周年,PHP是世界上最好的语言
- blender 命令行渲染
- html播放mp4不显示画面,浏览器播放mp4格式视频时只有声音看不到画面的原因及解决方法(精)...
- MySQL学习笔记①_案例记录
- ecshop mysql 操作_ecshop数据库操作函数
- if控制条件——BMI
- 双向迭代器实现对该商品名称数组的双向(向前和向后)遍历。
- 秦曾昌人工智能课程---7、决策树集成学习Tree Ensembles