参考资料:
《Spring事务源码解析之事务提交篇》
《Spring事务源码解析之事务回滚篇》
《spring事务(四):事务的隔离级别与传播方式的处理02》
《spring事务(五):事务的隔离级别与传播方式的处理03》
前文:
《Spring事务源码:创建代理类》
《Spring事务源码:事务创建》
写在开头:本文为个人学习笔记,内容比较随意,夹杂个人理解,如有错误,欢迎指正。
在前文中,我们介绍了事务的创建,事务创建好后就可以执行被代理方法了,执行完后我们还需要根据其执行结果判断是提交还是回滚,本文我们就来深入讲解下这两个操作。
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
// 声明式事务处理
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// 创建事务,事务属性等信息会被保存进TransactionInfo中
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal;
try {
// 执行目标方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 异常处理
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 清空事务信息
cleanupTransactionInfo(txInfo);
}
// 提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
目录
前言
一、事务回滚
1、completeTransactionAfterThrowing
2、rollback
3、processRollback
3.1、doRollback
3.2、rollbackToHeldSavepoint
3.3、doSetRollbackOnly
二、事务提交
1、commitTransactionAfterReturning
2、commit
3、processCommit
4、doCommit
三、清除事务信息
1、cleanupAfterCompletion
1.1、doCleanupAfterCompletion
1.2、resume
2、cleanupTransactionInfo
首先来看回滚,在invocation.proceedWithInvocation()异常后将会进入completeTransactionAfterThrowing方法,回滚正是由该方法完成的。
首先判断事务与事务属性是否存在,如果不存在自然也不需要回滚。如果存在则继续判断是否满足回滚的条件满足的话则调用rollback方法进行回滚。
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());
}
// 其余代码
}
else {
try {
// 如果不满足回滚条件出现异常也会继续提交
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
// 其余代码
}
}
}
rollbackOn通过判断异常是否为RuntimeException或Error来决定是否可以回滚,这个条件我们也可以自行配置。
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
首先判断当前事务是否已经完成,如果已经完成则属于异常,否则继续执行processRollback开始回滚。
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);
}
processRollback方法判断有无保存点,是全部回滚还是只回滚到保存点,亦或者需要等到事务链执行完成之后再做回滚。
private void processRollback(DefaultTransactionStatus status) {
try {
try {
// //解绑线程和会话绑定关系
triggerBeforeCompletion(status);
if (status.hasSavepoint()) {
// 如果有保存点,也就是当前事务为单独的线程则会退到保存点
status.rollbackToHeldSavepoint();
}
else if (status.isNewTransaction()) {
// 如果是新事务直接回滚
doRollback(status);
}
else if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
// 如果当前事务不是独立的事务,则只能等待事务链执行完成之后再做回滚操作
doSetRollbackOnly(status);
}
// 其余代码
// 关闭会话工厂,关闭会话,重置属性
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
}
finally {
// 清理并恢复挂起的事务
cleanupAfterCompletion(status);
}
}
回滚操作很简介,获取当前线程的数据库连接并调用其rollback方法进行回滚,使用的是底层数据库连接提供的API。
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);
}
}
和上文的doRollback类似,只不过这里多了一个保存点的限制,事务不是全部回滚的而是回滚到保存点即可。
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);
}
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);
}
}
doSetRollbackOnly作用在于人工的设置了回滚标志,不会继续向上抛出异常但是在事务链执行完后进行回滚。
protected void doSetRollbackOnly(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
if (status.isDebug()) {
logger.debug("Setting JDBC transaction [" + txObject.getConnectionHolder().getConnection() +
"] rollback-only");
}
txObject.setRollbackOnly();
}
// DataSourceTransactionObject.java
public void setRollbackOnly() {
getConnectionHolder().setRollbackOnly();
}
和回滚一样,先判断有无事务后再做提交操作。
protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
if (txInfo != null && txInfo.hasTransaction()) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
// 提交事务
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
如果发现事务链中存在回滚标记则表明需要回滚,调用processRollback方法回滚,具体内容上文做过介绍了这里不再赘述。判断无需回滚后就可以进行提交了。
public final void commit(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;
// 发现回滚标记
if (defStatus.isLocalRollbackOnly()) {
// 回滚操作
processRollback(defStatus);
return;
}
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
}
// 回滚操作
processRollback(defStatus);
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
return;
}
// 提交操作
processCommit(defStatus);
}
判断为独立事务则提交。
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
boolean globalRollbackOnly = false;
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
globalRollbackOnly = status.isGlobalRollbackOnly();
}
if (status.hasSavepoint()) {
// 释放保存点信息
status.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) {
// 独立事务则提交
doCommit(status);
}
if (globalRollbackOnly) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}
else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}
catch (RuntimeException ex) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
// 提交异常则回滚
doRollbackOnCommitException(status, ex);
throw ex;
}
catch (Error err) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, err);
throw err;
}
try {
triggerAfterCommit(status);
}
finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
finally {
// 清理事务信息
cleanupAfterCompletion(status);
}
}
获取数据库连接后调用底层API提交事务。
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
// 获取当前数据库连接
Connection con = txObject.getConnectionHolder().getConnection();
try {
// 调用底层API提交事务
con.commit();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not commit JDBC transaction", ex);
}
}
清除事务信息首先将事务状态设置为完成,然后重置数据库连接信息,如果有挂起的事务则还需要将其恢复。
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());
}
}
doCleanupAfterCompletion方法在新事务完成后调用resetConnectionAfterTransaction方法重置数据库连接信息,并判单如果是新的数据库连接则将其放回连接池。
protected void doCleanupAfterCompletion(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
if (txObject.isNewConnectionHolder()) {
// 将数据库连接从当前线程中解除绑定
TransactionSynchronizationManager.unbindResource(this.dataSource);
}
Connection con = txObject.getConnectionHolder().getConnection();
try {
// 恢复数据库连接的autoCommit状态
if (txObject.isMustRestoreAutoCommit()) {
con.setAutoCommit(true);
}
// 重置数据库连接信息,包括隔离级别、readOnly属性等
DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
}
catch (Throwable ex) {
logger.debug("Could not reset JDBC Connection after transaction", ex);
}
if (txObject.isNewConnectionHolder()) {
// 如果是新的数据库连接则将数据库连接放回连接池
DataSourceUtils.releaseConnection(con, this.dataSource);
}
txObject.getConnectionHolder().clear();
}
resetConnectionAfterTransaction方法负责重置数据库连接信息,包括隔离级别、readOnly属性。
public static void resetConnectionAfterTransaction(Connection con, Integer previousIsolationLevel) {
Assert.notNull(con, "No Connection specified");
try {
if (previousIsolationLevel != null) {
con.setTransactionIsolation(previousIsolationLevel);
}
if (con.isReadOnly()) {
con.setReadOnly(false);
}
}
catch (Throwable ex) {
logger.debug("Could not reset JDBC Connection after transaction", ex);
}
}
如果事务执行前有事务挂起,那么当前事务执行结束后需要将挂起的事务恢复,挂起事务时保存了原事务信息,重置了当前事务信息,所以恢复操作就是将当前的事务信息设置为之前保存的原事务信息。
protected final void resume(Object transaction, SuspendedResourcesHolder resourcesHolder)
throws TransactionException {
// 判断是否有事务挂起
if (resourcesHolder != null) {
Object suspendedResources = resourcesHolder.suspendedResources;
if (suspendedResources != null) {
// 恢复挂起事务
doResume(transaction, suspendedResources);
}
List suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
if (suspendedSynchronizations != null) {
TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
doResumeSynchronization(suspendedSynchronizations);
}
}
}
doResume方法重新绑定数据库连接与挂起的事务。
protected void doResume(Object transaction, Object suspendedResources) {
TransactionSynchronizationManager.bindResource(this.dataSource, suspendedResources);
}
cleanupTransactionInfo方法清除当前节点的事务信息,将旧事务节点信息通过 threadLocal 更新到当前线程。
protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {
if (txInfo != null) {
// 从当前线程的 ThreadLocal 获取上层的事务信息,将当前事务出栈,继续执行上层事务
txInfo.restoreThreadLocalStatus();
}
}
private void restoreThreadLocalStatus() {
// 当前事务处理完之后,恢复上层事务上下文
transactionInfoHolder.set(this.oldTransactionInfo);
}