问题抛出
在 Spring 的声明式事务中手动捕获异常,居然判定回滚了,例如 A 里面调用 B ,C ,C 里面抛出了异常,A 里面对 C 进行了 try catch 依然会回滚,上代码
@EnableTransactionManagement
@SpringBootApplication
public class BingApplication {public static void main(String[] args) {SpringApplication.run(BingApplication.class, args);}
}@Controller
public class IndexController {@AutowiredServiceA serviceA;@RequestMapping("test")@ResponseBodypublic String test() {serviceA.operate();return "OK";}}@Service
public class ServiceA {@Autowiredprivate ServiceB serviceB;@Autowiredprivate ServiceC serviceC;@Transactionalpublic void operate() {serviceB.insertB();try {serviceC.insertC();} catch (Exception e) {e.printStackTrace();}}
}@Service
public class ServiceB {@AutowiredImageMapper imageMapper;@Transactionalpublic void insertB() {Image image = new Image();image.setDescription("descriptionb");image.setDate("dateb");image.setUrl("urlb");imageMapper.insert(image);}
}@Service
public class ServiceC {@AutowiredImageMapper imageMapper;@Transactionalpublic void insertC() {Image image = new Image();image.setDescription("descriptionc");image.setDate("datec");image.setUrl("urlc");imageMapper.insert(image);throw new RuntimeException();}
}
正常情况下,因为我们对 insertC 进行了 try catch,所以我们期望 b 都能插入数据成功,但是都没插入成功,证明被回滚了
@Transactional 默认的传播机制是 Propagation.REQUIRED,REQUIRED 的意思是当前没有事物就创建新的事物,当前有事物
就加入事物。
源码分析
事物的代理入口类是 TransactionInterceptor,该类通过 ProxyTransactionManagementConfiguration 注册到容器中
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {TransactionInterceptor interceptor = new TransactionInterceptor();interceptor.setTransactionAttributeSource(transactionAttributeSource);if (this.txManager != null) {interceptor.setTransactionManager(this.txManager);}return interceptor;
}
里面重要的是 invoke 方法,invoke 方法中重要的是 invokeWithinTransaction 方法
@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 TransactionManager tm = determineTransactionManager(txAttr);。。。。。。PlatformTransactionManager ptm = asPlatformTransactionManager(tm);final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);// 注解的事务会走这里if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {// 开启事务// Standard transaction demarcation with getTransaction and commit/rollback calls.TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);Object retVal;try {// This is an around advice: Invoke the next interceptor in the chain.// This will normally result in a target object being invoked.retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// target invocation exception// 回滚completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {cleanupTransactionInfo(txInfo);}if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {// Set rollback-only in case of Vavr failure matching our rollback rules...TransactionStatus status = txInfo.getTransactionStatus();if (status != null && txAttr != null) {retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);}}// 提交事务commitTransactionAfterReturning(txInfo);return retVal;}。。。。。。
}
createTransactionIfNecessary 中有个 getTransaction
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)throws TransactionException {// Use defaults if no transaction definition given.TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());Object transaction = doGetTransaction();boolean debugEnabled = logger.isDebugEnabled();// 如果存在事务if (isExistingTransaction(transaction)) {// 处理存在事务的情况// Existing transaction found -> check propagation behavior to find out how to behave.return handleExistingTransaction(def, transaction, debugEnabled);}// Check definition settings for new transaction.if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());}// No existing transaction found -> check propagation behavior to find out how to proceed.if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");}// 第一次大部分走这里else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {SuspendedResourcesHolder suspendedResources = suspend(null);if (debugEnabled) {logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);}try {return startTransaction(def, transaction, debugEnabled, suspendedResources);}catch (RuntimeException | Error ex) {resume(null, suspendedResources);throw ex;}}else {// Create "empty" transaction: no actual transaction, but potentially synchronization.if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {logger.warn("Custom isolation level specified but no actual transaction initiated; " +"isolation level will effectively be ignored: " + def);}boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);}
}
我们第一次进来的时候是没有事务的,所以会新建事务 startTransaction
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);// 开启一个新的事务,这里有的参数 newTransaction 传入的是 trueDefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);doBegin(transaction, definition);prepareSynchronization(status, definition);return status;
}
第二次进来,因为有事务了,就会走到 handleExistingTransaction, 默认的 REQUIRED 就末尾的这一行
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
这里是传入的是 false,最后,我们返回 commitTransactionAfterReturning 方法中,看 commit 方法 中的 processCommit
// 如果有回滚点
if (status.hasSavepoint()) {if (status.isDebug()) {logger.debug("Releasing transaction savepoint");}unexpectedRollback = status.isGlobalRollbackOnly();status.releaseHeldSavepoint();
}
// 如果是新的事务
else if (status.isNewTransaction()) {if (status.isDebug()) {logger.debug("Initiating transaction commit");}unexpectedRollback = status.isGlobalRollbackOnly();doCommit(status);
}
else if (isFailEarlyOnGlobalRollbackOnly()) {unexpectedRollback = status.isGlobalRollbackOnly();
}
因为 newTransaction 不为 true,我们看异常情况下的回滚 在 org.springframework.transaction.support.AbstractPlatformTransactionManager#commit() 方法中的 processRollback
最终会走到 doSetRollbackOnly
doSetRollbackOnly -> DataSourceTransactionObject.setRollbackOnly() -> ResourceHolderSupport.setRollbackOnly()
public void setRollbackOnly() {this.rollbackOnly = true;
}
所以最后我们提交的时候,在 AbstractPlatformTransactionManager#commit(TransactionStatus status) 方法中
if (defStatus.isLocalRollbackOnly()) {if (defStatus.isDebug()) {logger.debug("Transactional code has requested rollback");}processRollback(defStatus, false);return;
}
如果有 rollbackOnly = true ,所以都会回滚
总结和其他
不同的情况
如果 A 有事物,B 有事物,C 事务,在 A 里面调用 B、C,C抛出运行时异常,即使被 try catch,数据不会插入
如果 A 有事物,B 有事物,C 事务,在 A 里面调用 B、C,C抛出编译时异常,即使被 try catch,数据也会插入
运行时异常 编译时异常
比如 RuntimeException 时 运行时异常,IOException 是编译时异常
如果 A 有事物,B 有事物,C 无事务,在 A 里面调用 B、C,C抛出运行时异常,如果不对 C 进行 try catch,数据不会插入
如果 A 有事物,B 有事物,C 无事务,在 A 里面调用 B、C,C抛出运行时异常,如果对 C 进行 try catch,数据都会插入
来源
https://blog.csdn.net/weixin_64314555/article/details/122492760