1:事务原理

1.2:aop/动态代理

类路径:org/springframework/aop/framework/CglibAopProxy.java


ReflectiveMethodInvocation#proceed 后续:


1.2:threadLocal


1.3:事务核心代码

org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction

    /**
* 每个被 @Transactional 修饰的方法都会走一遍 transaction interceptor,然后新增一个事务节点。
* 每个事务节点执行前都会判断是否需要新建事务、开启事务。
**/
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable { // If the transaction attribute is null, the method is non-transactional.
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr); if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// 创建一个事务信息对象,每一次调用 @Transactional 注解修饰的方法,都会重新创建一个 TransactionInfo 对象。
// 若有调用链有多个 @TransactionInfo 修饰的方法,则会形成一个事务链。
// 将最新的事务节点信息通过 threadLocal 更新到当前线程 。
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); Object retVal;
try {
// 真正执行crud语句的过程
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 抛异常之后决定是否回滚还是继续提交
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 清除当前节点的事务信息,将旧事务节点信息通过 threadLocal 更新到当前线程。
cleanupTransactionInfo(txInfo);
}
// 事务链执行完毕之后
commitTransactionAfterReturning(txInfo);
return retVal;
}else {
...
}
}

org.springframework.transaction.interceptor.TransactionAspectSupport.TransactionInfo

    protected final class TransactionInfo {

        // 事务管理器
@Nullable
private final PlatformTransactionManager transactionManager; @Nullable
private final TransactionAttribute transactionAttribute; // 切点信息(类路径#方法名称)
private final String joinpointIdentification; // 当前事务节点状态(是否完成、)
@Nullable
private TransactionStatus transactionStatus; // 旧事务节点/前事务节点
@Nullable
private TransactionInfo oldTransactionInfo;
}

1.4:事务链-图

2:事务回滚场景

用两个Service进行测试:

/**
* 模拟 Service A
**/
@Service
public class AopiService {
private final Logger log = LoggerFactory.getLogger(this.getClass()); @Resource(name = AopiRepositry.PACKAGE_BEAN_NAME)
private AopiRepositry aopiRepositry;
@Resource
private PmsTestService pmsTestService;
@Resource
private AopiService aopiService; ...
} /**
* 模拟 Service B
**/
@Service
public class PmsTestService { @Transactional(rollbackFor = Exception.class)
public void insertWithTransactional() {
int i = 1 / 0;
} public void insertWithoutTransactional() {
int i = 1 / 0;
}
}

模拟场景:

1:模拟ServiceA调用ServiceB(会异常,被try-catch),这种情况会回滚吗?

2:模拟ServiceA中调用自己的本类中的方法(会异常,被try-catch),这种情况会回滚吗?

3:模拟ServiceA注入自己并通过依赖的ServiceA调用另一个方法(会异常,被try-catch),这种情况会回滚吗?


2.1:场景1-1

    /**
* serviceA 加了 @Transactional
* serviceB 加了 @Transactional
* 最终:回滚
**/
@Transactional(rollbackFor = Exception.class)
public void insertA() {
Aopi aopi = new Aopi();
aopi.setName("1");
aopi.setAge(23);
aopiRepositry.insert(aopi);
try {
pmsTestService.insertWithTransactional();
} catch (Exception e) {
log.error("插入报错", e);
}
// 判断事务是否回滚
if (TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()) {
log.info("事务回滚了");
} else {
log.info("事务没回滚");
}
}

2.2:场景1-2

    /**
* serviceA 加了 @Transactional
* serviceB 没加 @Transactional,但是手动 throw e;
* 最终:回滚
**/
@Transactional(rollbackFor = Exception.class)
public void insertAA() {
Aopi aopi = new Aopi();
aopi.setName("1");
aopi.setAge(23);
aopiRepositry.insert(aopi);
try {
pmsTestService.insertWithoutTransactional();
} catch (Exception e) {
log.error("插入报错", e);
throw e;
}
}

2.3:场景1-3

    /**
* serviceA 加了 @Transactional
* serviceB 没加 @Transactional,但是手动 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
* 最终:回滚
* <p>
* 若不手动 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(),那么不会回滚
**/
@Transactional(rollbackFor = Exception.class)
public void insertAAA() {
Aopi aopi = new Aopi();
aopi.setName("1");
aopi.setAge(23);
aopiRepositry.insert(aopi);
try {
pmsTestService.insertWithoutTransactional();
} catch (Exception e) {
log.error("插入报错", e);
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
// 判断事务是否回滚
if (TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()) {
log.info("事务回滚了");
} else {
log.info("事务没回滚");
}
}

2.4:场景2-1

    /**
* serviceA 加了 @Transactional
* 调用过程中被异常被捕获,并不抛出
* 最终:不回滚
**/
@Transactional(rollbackFor = Exception.class)
public void insertAAAA() {
Aopi aopi = new Aopi();
aopi.setName("1");
aopi.setAge(23);
aopiRepositry.insert(aopi);
try {
int i = 1 / 0;
} catch (Exception e) {
log.error("插入报错", e);
}
// 判断事务是否回滚
if (TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()) {
log.info("事务回滚了");
} else {
log.info("事务没回滚");
}
}

2.5:场景2-2

    /**
* 本类方法A 加了 @Transactional
* 本类方法B 加了 @Transactional,异常被捕获,并不抛出
* 最终:不回滚
* <p>
* 原因:调用 insert 并不会重新走代理调用(this 对象不是代理对象)
**/
@Transactional(rollbackFor = Exception.class)
public void insertAAAAA() {
Aopi aopi = new Aopi();
aopi.setName("1");
aopi.setAge(23);
aopiRepositry.insert(aopi);
try {
insert();
} catch (Exception e) {
log.error("插入报错", e);
}
// 判断事务是否回滚
if (TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()) {
log.info("事务回滚了");
} else {
log.info("事务没回滚");
}
}

2.6:场景3-1

    /**
* 本类方法A 加了 @Transactional
* 自己注入自己,再调用本类方法B,加了 @Transactional,异常被捕获,并不抛出
* 最终:回滚
* <p>
* 原因:aopiService bean 是一个代理bean,每次调用都会重新走代理调用逻辑。
**/
@Transactional(rollbackFor = Exception.class)
public void insertAAAAAA() {
Aopi aopi = new Aopi();
aopi.setName("1");
aopi.setAge(23);
aopiRepositry.insert(aopi);
try {
aopiService.insert();
} catch (Exception e) {
log.error("插入报错", e);
}
// 判断事务是否回滚
if (TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()) {
log.info("事务回滚了");
} else {
log.info("事务没回滚");
}
} @Transactional(rollbackFor = Exception.class)
public void insert() {
int i = 1 / 0;
}

3:结论

1:程序异常,事务是否回滚取决于 当前线程的事务状态。

2:异常是否抛出并不是并不是一定会导致回滚失败的原因。即使异常被捕获,且不再次throw,事务也可能回滚。

3:抛出的异常不在rollback 范围内,也不会进行回滚。


其他:

1:spring 用的cglib代理库是自己的库(依赖于spring-aop的包),并没有引用第三方cglib库。

Spring 事务回滚机制详解的更多相关文章

  1. Spring事务回滚和异常类

    1.异常的一些基本知识 异常的架构 异常的继承结构:Throwable为基类,Error和Exception继承Throwable.Error和RuntimeException及其子类成为未检查异常( ...

  2. 事务之二:spring事务(事务管理方式,事务5隔离级别,7个事务传播行为,spring事务回滚条件)

    事物管理对于企业应用来说是至关重要的,好使出现异常情况,它也可以保证数据的一致性. spring支持编程式事务管理和声明式事务管理两种方式. 编程式事务管理使用TransactionTemplate或 ...

  3. Spring中的事务回滚机制

    初学者笔记 问题:在Java项目汇中,添加@Transactional注解,报错之后,事务回滚未生效,数据仍插入数据库中.经查看报错位置位于新增成功之后.空指针异常. 一.特性 先了解一下@Trans ...

  4. java方法 throws exception 事务回滚机制

    使用spring难免要用到spring的事务管理,要用事务管理又会很自然的选择声明式的事务管理,在spring的文档中说道,spring声明式事务管理默认对非检查型异常和运行时异常进行事务回滚,而对检 ...

  5. @Transactional注解不回滚原因详解

    最近试了试spring的回滚功能,根据网上的教程配置怎么都不好使,遂寻找答案, 网上的答案都是这么讲的: 1. 检查你方法是不是public的. 2. 你的异常类型是不是unchecked异常.如果我 ...

  6. spring事务回滚问题

    刚刚接到一个上家公司同事的一个电话,问我为什么service方法事务不会滚了,日志打印了,调用webservice报错. 我让他把这个调用执行webservice的方法截图发给我,如下:   publ ...

  7. mysql事务回滚机制概述

    应用场景:   银行取钱,从ATM机取钱,分为以下几个步骤       1 登陆ATM机,输入密码:    2 连接数据库,验证密码:    3 验证成功,获得用户信息,比如存款余额等:    4 用 ...

  8. @Transactional spring事务回滚相关

    还可以设置回滚点,看下面 /** * 用户登录接口 * * * 1明确事务方法前的命名规则 * 2保证事务方法执行的时间尽可能的短,不允许出现循环操作,不允许出现RPC等网络请求操作 * 3不允许所有 ...

  9. Myql 中的事务回滚机制概述 ?

    事务是用户定义的一个数据库操作序列,这些操作要么全做要么全不做,是一个 不可分割的工作单位,事务回滚是指将该事务已经完成的对数据库的更新操作撤 销. 要同时修改数据库中两个不同表时,如果它们不是一个事 ...

随机推荐

  1. eladmin-plus V2.0.0 发布,单表链式调用更丝滑

    一.项目简介 eladmin的mybatis-plus版本,单表使用链式调用,代码更简洁,调用更便捷.目前更新到2021年7月.项目基于 Spring Boot 2.4.2 . Mybatis-plu ...

  2. jQuery 两个日期时间相减

    var sDate='2016-10-31';var eDate='2016-10-10'var sArr = sDate.split("-");var eArr = eDate. ...

  3. 如何从二维平面n个点中寻找距离最近两个点?

    如何理解分治算法 什么是分治算法?简单来说就是"分而治之",也就是将原问题划分成n个规模较小的,并且结构与原问题相似的子问题,然后去递归地解决这些子问题,最后再合并其结果,就得到原 ...

  4. 计算机网络笔记Part1 概述

    总目录 1.计算机网络的功能.组成.分类 1.1功能 数据通信 资源共享 分布式处理 提高可靠性 负载均衡 1.2组成部分 硬件 软件 协议 1.3分类 按分布范围 广域网 WAN 城域网 MAN 局 ...

  5. MySQL 索引使用案例

    索引使用案例 支持多种过滤条件 假设要设计一个在线约会网站,用户信息表有很多列,包括国家.地区.城市.性别.眼睛颜色,等等.网站必须支持上面这些特征的各种组合来搜索用户,还必须允许根据用户的最后在线时 ...

  6. Flutter开发进阶学习指南Flutter开发进阶学习指南

    Flutter 的起源 Flutter 的诞生其实比较有意思,Flutter 诞生于 Chrome 团队的一场内部实验, 谷歌的前端团队在把前端一些"乱七八糟"的规范去掉后,发现在 ...

  7. 雪花算法ID在前端丢失精度解决方案

    首先说一下背景,目前笔者的工作是物联网方面的,设备有对应的智慧运营平台,平台开发中建表的主键用的是Mybatis plus默认的雪花算法来生成的,也就是分布式系统比较常用的雪花ID,技术栈就是常用的S ...

  8. [C++]-unordered_map 映射

    unordered_map和map的区别请点击这里. 本文中的代码跟[C++]-map 映射中的代码仅仅是把定义的map类型数据定义成了unordered_map类型数据. 代码 #include&l ...

  9. The Apache Tomcat Native library which allows using OpenSSL was not found on the java.library.path 问题解决记录

    1.问题 启动Tomcat之后,在浏览器输入IP后显示503,查看catalina.log发现报错: 2.问题定位:缺少 tomcat-native library 就是说 缺少Tomcat Nati ...

  10. 由”二进制里不能有3“引发的对parseInt的思考

    看到一道面试题,["1", "2", "3"].map(parseInt) 答案是多少? 心生好奇,做做看,发现卡住,没什么头绪.首先对pa ...