案例二:try-required开始敲黑板划重点了 , 这个情况也是大家最常见的情况之一 , 但是由于这个try起来了 , 异常不会抛出 , 那么 , 这个insert能否插入数据呢?
@Transactional@Overridepublic void Example2(User user) {userMapper.insert(user);try {propagationService.required();} catch (Exception e) {e.printStackTrace();}}
@Transactional@Overridepublic void required() {throw new NullPointerException;}
单元测试
@Testpublic void testExample2() throws Exception {User user = new User();user.setName;userService.Example2(user);}
案例三:try-requiresNew这个案例和上面的案例很相似 , 区别在于 , 这里的隔离级别是Propagation.REQUIRES_NEW , 那么 , 这个insert能否插入数据呢?
@Transactional@Overridepublic void Example3(User user) {userMapper.insert(user);try {propagationService.requiresNew();} catch (Exception e) {e.printStackTrace();}}
【Java程序员只会CRUD连Spring事务传播机制都不懂?】@Transactional(propagation = Propagation.REQUIRES_NEW)@Overridepublic void requiresNew() {throw new NullPointerException;}
单元测试
@Testpublic void testExample3() throws Exception {User user = new User();user.setName;userService.Example3(user);}
案例四:常规情况这个和案例一很想 , 结果会有差别吗?最后insert能插入吗?
@Transactional@Overridepublic void Example4(User user) {userMapper.insert(user);propagationService.requiresNew();}
@Transactional(propagation = Propagation.REQUIRES_NEW)@Overridepublic void requiresNew() {throw new NullPointerException;}
单元测试
@Testpublic void testExample4() throws Exception {User user = new User();user.setName;userService.Example4(user);}
解密及大白话说明原理案例一这个不用说 , 稍微有点JAVA常识的人都知道 , 异常必然会导致回滚 , 数据库不会插入数据 。
案例二这个到底会不会插入数据呢?毕竟这个异常被try起来了 。这个时候 , 正常的思维都会认为 , 能正常插入数据 , 但是答案是 , 不会插入数据 , 并且抛出异常
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
为啥会这样呢?
案例三这个和案例二很像 , 因为有了案例二的阴影 , 这个时候你就变得不确定了 。答案是 , 能正常插入数据 。
案例四这个和案例一很像 , 本来你是很确定能不能插入数据的 , 但是有了案例三的阴影之后 , 这个时候你又变得不确定了 。答案是 , 不会插入数据 。
原理!这四个案例 , 只要你把这四个案例和原理弄熟 , 再复杂的各种try模型下 , 事务是否回滚 , 你都清清楚楚 。
我们先来看看@Transactional的核心方法
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {// 开启事务TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);Object retVal = null;try {// 执行业务方法retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// 回滚事务completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {cleanupTransactionInfo(txInfo);}// 提交事务commitTransactionAfterReturning(txInfo);return retVal; }
我们重点解析案例二和案例三的情况 。我们再把那两个事务传播机制的意思来解读一下:
PROPAGATION_REQUIRED:Spring的默认传播级别 , 如果上下文中存在事务则加入当前事务 , 如果不存在事务则新建事务执行 。
PROPAGATION_REQUIRES_NEW:该传播级别每次执行都会创建新事务 , 并同时将上下文中的事务挂起 , 执行完当前线程后再恢复上下文中事务 。首先来看案例二 , 执行Example2方法的时候 , 由上文得知 , 将开启一个事务 , 再执行到required方法时 , 此时 , 因为用得的默认的隔离级别 , 因此 , 这个时候 , 会加入到刚才的事务之中 , 然后required方法中 , 出现了异常 , 我们来看
completeTransactionAfterThrowing(txInfo, ex);
中的核心方法文章插图
推荐阅读
- JAVA技术总监:技术团队管理的6大坑
- 程序员最常见的谎话
- 36岁互联网开发大叔感慨:今年Java面试,技术要求都这么高?
- 现在程序员进行数据分析都在用Excel?Python才是时代的趋势
- 华为鸿蒙应用开发的JavaScript UI设计概述
- 七个最有用的JavaScript技巧
- CDN与缓存全面详聊,看完之后,大部分程序员收藏了
- 网络爬虫 JAVA网络编程模拟浏览器获取html源代码
- Java实现基于token认证
- 什么是真正的架构设计?十年Java经验让我总结出了这些