死磕Spring之AOP篇 - Spring 事务详解 (17)

整个过程在提交事务的前后会进行相关处理,例如清理资源;对于嵌套事务,这里会释放保存点,等外层的事务进行提交;对于新的事务,这里会调用Connection#commit()方法提交事务;其他情况不会真的提交事务,在这里仅清理相关资源,唤醒被挂起的资源

3. 回滚事务

在 TransactionInterceptor 事务拦截处理过程中,如果方法的执行过程抛出异常,那么此时我们是不是需要调用 Connection#roback() 方法,对本次事务进行回滚,我们一起来看看 Spring 的处理过程

// TransactionAspectSupport.java protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) { if (txInfo != null && txInfo.getTransactionStatus() != null) { // 如果 @Transactional 配置了需要对那些异常进行回退,则需要判断抛出的异常是否匹配 // 没有配置的话只处理 RuntimeException 或者 Error 两种异常 // <1> 如果异常类型匹配成功,则进行回滚 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; } } // <2> 否则,不需要回滚,提交事务 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; } } } }

处理异常的过程如下:

如果异常类型匹配成功,则进行回滚,如果 @Transactional 配置了需要对哪些异常进行回退,则需要判断抛出的异常是否匹配,没有配置的话只处理 RuntimeException 或者 Error 两种异常

回滚操作,调用 AbstractPlatformTransactionManager#rollback(TransactionStatus) 方法

否则,异常类型不匹配

还是进行提交操作, 调用 AbstractPlatformTransactionManager#commit(TransactionStatus) 方法,在上面的“提交事务”小节中已经讲过

可以看到,出现了异常不一定会回滚,需要异常类型匹配

3.1 rollback 方法 // AbstractPlatformTransactionManager.java @Override public final void rollback(TransactionStatus status) throws TransactionException { // <1> 如果事务已完成,此时又回滚,则抛出异常 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; // <2> 进行回滚(预料之中) processRollback(defStatus, false); } 3.2 processRollback 方法 // AbstractPlatformTransactionManager.java private void processRollback(DefaultTransactionStatus status, boolean unexpected) { try { boolean unexpectedRollback = unexpected; try { // <1> 调用 TransactionSynchronization#beforeCompletion 方法 // 由 Spring 事务托管,不是真的关闭连接,从 ThreadLocal 中删除 DataSource 和 ConnectionHolder 的映射关系 // 例如在 Mybatis-Spring 中的 SqlSessionSynchronization 中,会从 ThreadLocal 中删除 SqlSessionFactory 和 SqlSessionHolder 的映射关系, // 且调用其 SqlSession#close() 方法 triggerBeforeCompletion(status); // <2> 如果有 Savepoint 保存点 if (status.hasSavepoint()) { // 回滚到保存点,调用 Connection#rollback(Savepoint) 方法 status.rollbackToHeldSavepoint(); } // <3> 否则,如果是新的事务,例如传播级别为 **REQUIRED_NEW** 则一定是一个新的事务 else if (status.isNewTransaction()) { // 事务回滚,调用 Connection#rollback() 方法 doRollback(status); } // <4> 否则,不是新的事务也没有保存点,那就是加入到一个已有的事务这种情况,例如 **REQUIRED** 传播级别,如果已存在一个事务,则加入其中 else { // Participating in larger transaction if (status.hasTransaction()) { // 如果已经标记为回滚,或当加入事务失败时全局回滚(默认 true) if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) { // 设置当前 ConnectionHolder#rollbackOnly 为 true // 在这个事务提交的时候进行回滚 doSetRollbackOnly(status); } else { } } else { } // Unexpected rollback only matters here if we're asked to fail early // 在事务被标记为全局回滚的情况下是否提前失败 // 默认为 false,表示不希望抛出异常 if (!isFailEarlyOnGlobalRollbackOnly()) { // 那么设置 unexpectedRollback 为 false unexpectedRollback = false; } } } // 运行时异常或者其它异常 catch (RuntimeException | Error ex) { triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); throw ex; } // 触发完成后的回调,事务同步状态为已提交,调用 TransactionSynchronization#afterCompletion 方法 // 例如在 Mybatis-Spring 中的 SqlSessionSynchronization 中,会从 ThreadLocal 中删除 SqlSessionFactory 和 SqlSessionHolder 的映射关系, // 且调用其 SqlSession#close() 方法,解决可能出现的跨线程的情况 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); } }

回退过程如下:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zygsff.html