这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战
前言
继上篇博客之后,本文主要是详解 NESTED这个事务传播机制
和前文一样的四个问题
-
子事务提交,父事务会提交么?
-
父事务提交,子事务会提交么?
-
子事务回滚,父事务会回滚么?
-
父事务会回滚,子事务会回滚么?
文本中所说的前文是:spring 事务源码(五),如果大家没有看过建议先看看,因为NESTED和REQUIRE很相似,只有一点不同
伪代码
最外层的事务
@Transactional
method_a(){
method_b();
}
调用的子事务
@Transactional(propagation = Propagation.NESTED)
method_b(){
}
savePoint(备份点)
这里先说一个概念,savePoint,暂且称之为 备份点
是很多db都支持的一种机制,可以回滚到指定的备份点,类似操作系统的备份
当事务指定回滚到备份点一的时候,那么发生在备份点一后的备份点二、三所做操作全部回滚
可以参考
源码分析
getTransaction
区别点在于
handleExistingTransaction
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
.....
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
....
// 是否保存备份点
if (useSavepointForNestedTransaction()) {
//
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
// 创建一个备份点
status.createAndHoldSavepoint();
return status;
}
else {
// 这种情况暂时不考虑,还没搞懂
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, null);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
}
......
}
执行业务sql
因为并没有修改线程中的connect holder所以method_b 中的所有操作和method_a 是在同一个db事务
提交事务
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
....
if (status.hasSavepoint()) {
unexpectedRollback = status.isGlobalRollbackOnly();
// 类似释放删除备份点
status.releaseHeldSavepoint();
}
.....
}
回滚
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
...
// method_b 回滚执行此方法
if (status.hasSavepoint()) {
status.rollbackToHeldSavepoint();
}
.....
}
此时回滚有多种可能,取决于业务代码
@Transactional(rollbackFor = Exception.class)
method_a(){
method_b();
}
@Transactional(rollbackFor = Exception.class)
method_b(){
}
先说这种代码形式,
先是事务回滚到备份点,然后因为 抛出异常未捕获,method_a 也回滚,那么全部回滚
@Transactional(rollbackFor = Exception.class)
method_a(){
try{
method_b();
}catch(){
}
}
@Transactional(rollbackFor = Exception.class)
method_b(){
}
如果是这种形式的话,就只会回滚method_b 中的事务
和REQUIRED_NEW结果是一样的