承接上文,Spring事务会根据目标业务方法的执行结果做提交或回滚操作,我们先看一下回滚操作:
TransactionAspectSupport:
protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
// 判断当前是否存在事务
if (txInfo != null && txInfo.hasTransaction()) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
/* 判断是否满足回滚条件 */
if (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 ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by rollback error", ex);
throw err;
}
}
else {
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 ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by commit error", ex);
throw err;
}
}
}
}
DefaultTransactionAttribute:
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
其中回滚条件默认为RuntimeException或Error,我们也可以自行配置。
AbstractPlatformTransactionManager:
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(defStatus); /* 执行回滚 */
}
AbstractPlatformTransactionManager:
private void processRollback(DefaultTransactionStatus status) {
try {
try {
// 激活所有TransactionSynchronization中对应的方法
triggerBeforeCompletion(status);
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
/* 如果有保存点,也就是当前事务为单独的线程则会退到保存点 */
status.rollbackToHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
/* 如果是新事务直接回滚 */
doRollback(status);
}
else if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
// 如果当前事务不是独立的事务,则只能等待事务链执行完成之后再做回滚操作
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");
}
}
catch (RuntimeException ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
catch (Error err) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw err;
}
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
}
finally {
/* 清理并恢复挂起的事务 */
cleanupAfterCompletion(status);
}
}
AbstractTransactionStatus:
public void rollbackToHeldSavepoint() throws TransactionException {
if (!hasSavepoint()) {
throw new TransactionUsageException(
"Cannot roll back to savepoint - no savepoint associated with current transaction");
}
/* 回退到保存点 */
getSavepointManager().rollbackToSavepoint(getSavepoint());
getSavepointManager().releaseSavepoint(getSavepoint());
setSavepoint(null);
}
JdbcTransactionObjectSupport:
public void rollbackToSavepoint(Object savepoint) throws TransactionException {
ConnectionHolder conHolder = getConnectionHolderForSavepoint();
try {
conHolder.getConnection().rollback((Savepoint) savepoint);
}
catch (Throwable ex) {
throw new TransactionSystemException("Could not roll back to JDBC savepoint", ex);
}
}
保存点一般用于嵌入式事务,内嵌事务的回滚不会引起外部事务的回滚。下面我们来看新事务的回滚:
DataSourceTransactionManager:
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方法进行回滚,使用的是底层数据库连接提供的API。最后还有一个清理和恢复挂起事务的操作:
AbstractPlatformTransactionManager:
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
status.setCompleted(); // 将当前事务状态设置为完成
if (status.isNewSynchronization()) {
// 清空当前事务信息
TransactionSynchronizationManager.clear();
}
if (status.isNewTransaction()) {
/* 新事务在事务完成之后做清理操作 */
doCleanupAfterCompletion(status.getTransaction());
}
if (status.getSuspendedResources() != null) {
if (status.isDebug()) {
logger.debug("Resuming suspended transaction after completion of inner transaction");
}
/* 将原事务从挂起状态恢复 */
resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());
}
}
DataSourceTransactionManager:
protected void doCleanupAfterCompletion(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
if (txObject.isNewConnectionHolder()) {
// 将数据库连接从当前线程中解除绑定
TransactionSynchronizationManager.unbindResource(this.dataSource);
}
Connection con = txObject.getConnectionHolder().getConnection();
try {
if (txObject.isMustRestoreAutoCommit()) {
con.setAutoCommit(true); // 恢复数据库连接的autoCommit状态
}
// 重置数据库连接信息,包括隔离级别、readOnly属性等
DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
}
catch (Throwable ex) {
logger.debug("Could not reset JDBC Connection after transaction", ex);
}
if (txObject.isNewConnectionHolder()) {
if (logger.isDebugEnabled()) {
logger.debug("Releasing JDBC Connection [" + con + "] after transaction");
}
// 如果是新的数据库连接则将数据库连接放回连接池
DataSourceUtils.releaseConnection(con, this.dataSource);
}
txObject.getConnectionHolder().clear();
}
AbstractPlatformTransactionManager:
protected final void resume(Object transaction, SuspendedResourcesHolder resourcesHolder)
throws TransactionException {
if (resourcesHolder != null) {
Object suspendedResources = resourcesHolder.suspendedResources;
if (suspendedResources != null) {
doResume(transaction, suspendedResources);
}
List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
if (suspendedSynchronizations != null) {
TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
doResumeSynchronization(suspendedSynchronizations);
}
}
}
如果事务执行前有事务挂起,那么当前事务执行结束后需要将挂起的事务恢复,挂起事务时保存了原事务信息,重置了当前事务信息,所以恢复操作就是将当前的事务信息设置为之前保存的原事务信息。到这里事务的回滚操作就结束了,下篇文章我们来分析Spring事务提交流程。