此篇文章,我们验证下它的严谨性
【场景一】
@Transactional(rollbackFor = Exception.class)
public void updateOrderStatus() {
try {
update order set status = 6 where id=1;
int i = 3 / 0;
} catch (Exception ignored) {}
}
【场景二】
@Transactional(rollbackFor = Exception.class)
public void updateOrderStatus() {
try {
update order set status = 6 where id=1;
service.calculate();
} catch (Exception ignored) {}
}
public void calculate() {
int i = 3 / 0;
}
【场景三】
@Transactional(rollbackFor = Exception.class)
public void updateOrderStatus() {
try {
update order set status = 6 where id=1;
service.calculate();
} catch (Exception ignored) {}
}
@Transactional(rollbackFor = Exception.class)
public void calculate() {
int i = 3 / 0;
}
假设 updateOrderStatus 和 calculate 方法的事务均生效,不考虑事务不生效的情况.
那么以上3种场景,方法 updateOrderStatus 执行结束之后,updateOrderStatus 方法是否能正常提交事务呢?
对于场景一和场景二来说,updateOrderStatus 方法可以正常提交,读者朋友应该不会有异议
@Transactional(rollbackFor = Exception.class)
public void updateOrderStatus() {
try {
update order set status = 6 where id=1;
service.calculate();
} catch (Exception ignored) {}
}
@Transactional(rollbackFor = Exception.class)
public void calculate() {
int i = 3 / 0;
}
假设 updateOrderStatus 方法是小赵写的方法, calculate 方法是项目中已有的功能方法,小赵只是调用了现有的 calculate 方法.
由于小赵的 updateOrderStatus 方法必须要保证,即便出现异常,也要提交事务,因此他使用了try … catch.
// org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// 调用业务方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 内部会将事务标记成回滚
completeTransactionAfterThrowing(txInfo, ex);
// 异常继续向上抛出
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
}
}
// completeTransactionAfterThrowing 方法会调用到 doSetRollbackOnly方法
// org.springframework.jdbc.datasource.DataSourceTransactionManager#doSetRollbackOnly
protected void doSetRollbackOnly(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject)status.getTransaction();
txObject.setRollbackOnly();
}
public void setRollbackOnly() {
this.getConnectionHolder().setRollbackOnly();
}
public void setRollbackOnly() {
this.rollbackOnly = true;
}
Spring将事务标记成了 rollbackOnly = true,即当前事务只能回滚.
即便外层的 updateOrderStatus 方法捕获了异常,一旦 updateOrderStatus 方法提交事务,就会提示如下错误
如果在异常发生的地方(即calculate 方法内部),和捕获异常的地方(即updateOrderStatus 方法内部),这中间如果有Spring的事务代码(比如使用了@Transactional ),那么Spring是第一个感知到异常,即便业务代码中加了try … catch…也无济于事,Spring依然不会允许提交事务. 如果这中间没有Spring的事务代码,即Spring没有感知到业务代码中的异常,而且业务代码中又使用了try … catch…,那么Spring就会允许提交事务.