关联博文:
SpringBoot中事务执行原理分析(一)
SpringBoot中事务执行原理分析(二)
SpringBoot中事务执行原理分析(三)
SpringBoot中事务执行原理分析(四)
SpringBoot中事务执行原理分析(五)
SpringBoot中事务执行原理分析(六)
SpringBoot中事务执行原理分析补充篇
你认真研究过Spring中的@EnableTransactionManagement注解吗?
接上文SpringBoot中事务执行原理分析(四)我们分析过方法正常执行完事务提交后,本文我们继续分析目标方法抛出异常后事务的回滚流程,即 completeTransactionAfterThrowing(txInfo, ex)
。
TransactionAspectSupport的completeTransactionAfterThrowing方法如下。
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
//如果事务信息对象或者事务状态对象为空,则不作处理
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
//判断当前异常类型是否需要回滚
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
//回滚动作的触发--交给事务管理器
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
//如果当前异常类型不需要回滚,则直接触发提交动作
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
}
}
}
这里首先对事务信息对象和事务状态对象做了判断,如果其中有一个为 null
则直接跳过不做处理。然后对异常类型做了判断,判断当前异常类型时触发 rollback 还是 commit动作。关于commit动作我们前面已经分析过了,本文我们分析rollback动作。
AbstractPlatformTransactionManager的rollback方法
@Override
public final void rollback(TransactionStatus status) throws TransactionException {
//判断事务状态是否为已完成,已完成则抛出异常
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
//交给了processRollback
processRollback(defStatus, false);
}
可以看到其rollback方法只是对status做了是否已经完成的状态校验,然后交给了processRollback方法。
AbstractPlatformTransactionManager的processRollback方法。
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
//初值false
boolean unexpectedRollback = unexpected;
try {
//遍历触发TransactionSynchronization的beforeCompletion方法
triggerBeforeCompletion(status);
//如果有savepoint则首先回滚到savepoint位置然后释放savepoint
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
status.rollbackToHeldSavepoint();
}
//如果事务是新事务,则触发rollBack动作
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
doRollback(status);
}
//如果当前事务是一个已经存在的事务
else {
// Participating in larger transaction
if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
//修改rollbackOnly状态为true
doSetRollbackOnly(status);
}
else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
}
else {
logger.debug("Should roll back transaction but cannot - no transaction available");
}
// Unexpected rollback only matters here if we're asked to fail early
if (!isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = false;
}
}
}
catch (RuntimeException | Error ex) {
//遍历触发TransactionSynchronization 的afterCompletion 状态是STATUS_UNKNOWN
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
//遍历触发TransactionSynchronization 的afterCompletion 状态是STATUS_ROLLED_BACK
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
// Raise UnexpectedRollbackException if we had a global rollback-only marker
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
}
finally {
// 完成后清理事务信息
cleanupAfterCompletion(status);
}
}
方法流程梳理如下:
- 遍历触发TransactionSynchronization的beforeCompletion方法
- 如果有savepoint则首先回滚到savepoint位置然后释放savepoint
- 如果事务是新事务,则触发rollBack动作
- 如果当前事务是一个已经存在的事务,则可能设置事务状态rollbackOnly为true
- 遍历触发TransactionSynchronization 的afterCompletion
- 完成后清理事务信息-cleanupAfterCompletion
可以看到,这里同前文提交分析流程类型,同样触发了TransactionSynchronization的钩子函数,对事务进行了判断处理最后清理事务资源。
cleanupAfterCompletion同前文一致,TransactionSynchronization我们另起篇章分析。我们接下来看下核心方法doRollback(status);
。
DataSourceTransactionManager的doRollback方法
@Override
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
方法如上所示其从事务对象里面获取到连接对象,然后触发数据库连接对象的rollback方法。
本文这里获取到的数据库连接对象是:HikariProxyConnection@2062395789 wrapping com.mysql.cj.jdbc.ConnectionImpl@12ee6186
。可以看到其内有委派对象delegate=ConnectionImpl
ProxyConnection 的rollback方法
@Override
public void rollback() throws SQLException
{
delegate.rollback();
isCommitStateDirty = false;
lastAccess = currentTime();
}
再往下就交给了MySQL驱动来实现这个功能,如下所示,其最终触发了this.session.execSQL(null, "rollback", -1, null, false, this.nullStatementResultSetFactory, null, false);