理解本篇文章需要的知识储备:
- JDBC,connection事务提交方式
- mybatis sqlsession的开启和关闭,相当于connection的一次开启和关闭
我们把事务传播过程中的外层称为调用者,内层称为被调用者
物理事务:一次connection(相当于mybatis的一次sqlsession)的开启和关闭,其间的所有数据库操作
逻辑事务:被@Transactional注解修饰的操作,具体根据传播行为来判断是否是逻辑事务
被调用者中(调用者已经存在事务),只有PROPAGATION_REQUIRES_NEW传播行为开启了一个新的物理事务,其他传播行为都是逻辑事务
下面通过代码分析
github代码地址,欢迎大家一起参与开源
Propagation.REQUIRED事务传播方式
调用者代码:
代码语言:txt复制@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void requestTransactional() {
userService.addUser(6, "user6");
userServiceTransactionStateInvoked.requestTransactional();
}
被调用者代码:
代码语言:txt复制@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public void requestTransactional() {
userService.addUser(6, "user6");
}
调用者和被调用者的事务传播行为都是:Propagation.REQUIRED
执行代码日志如下:
我们一步步分析两次插入数据代码执行流程:
- 创建SqlSession
- 注册事务到sqlsession
- 事务持有一个connection
- 开始执行第一次插入操作,并成功
- 释放当前sqlsession
- 发现第二次插入和第一次插入同属于要一个物理事务
- 第二次插入获取sqlsession
- 执行sql
- 释放sqlsession
- 发现事务中有方法执行失败
- JDBC标记事务需要回滚
- 事务取消注册
- sqlsession执行close操作
- JDBC回滚init
- connection执行回滚操作,执行完毕释放connection
执行过程中两次插入数据属于两个不同的逻辑事务,但是他们同属于一个物理事务(因为sqlsession和connection)始终是同一个。
一步步分析下来对逻辑事务和物理事务将会有一个直观的认识。
Propagation.REQUIRES_NEW事务传播方式
调用者代码:
代码语言:txt复制@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void requiresNewTransactional() {
userService.addUser(6, "user6");
userServiceTransactionStateInvoked.requiresNewTransactional();
userService.addUser(6, "user6");
}
被调用者代码:
代码语言:txt复制@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void requiresNewTransactional() {
userService.addUser(7, "user7");
}
执行代码日志如下:
分析3次插入数据代码执行流程的关键步骤:
- 创建sqlsession
- 注册事务
- 使用spring容器管理JDBC connection
- 第一次数据插入成功后释放sqlSession
- 暂停当前事务创建新的事务(***注意:这就是在创建新的物理事务***)
- 创建新的sqlsession
- 注册新事务
- 注册新的connection连接
- 第二次数据插入成功
- 关闭sqlsession,执行commit,释放connection
- 恢复第一次创建的事务,并执行第三次数据插入
- 关闭sqlsession(注意观察sqlsession唯一标识)
- 第三次数据插入失败,进行事务回滚,第一次数据虽然插入成功,但是和第二次数据插入同处同一个物理事务,所以也被回滚。
- 释放第一次的connection(注意观察connection唯一标识)
最终执行结果:第一次和第三次数据插入失败,只有第二次插入数据成功。