聊聊spring的UnexpectedRollbackException

本文主要研究一下spring的UnexpectedRollbackException

UnexpectedRollbackException

/**
 * Thrown when an attempt to commit a transaction resulted
 * in an unexpected rollback.
 *
 * @author Rod Johnson
 * @since 17.03.2003
 */
@SuppressWarnings("serial")
public class UnexpectedRollbackException extends TransactionException {

	/**
	 * Constructor for UnexpectedRollbackException.
	 * @param msg the detail message
	 */
	public UnexpectedRollbackException(String msg) {
		super(msg);
	}

	/**
	 * Constructor for UnexpectedRollbackException.
	 * @param msg the detail message
	 * @param cause the root cause from the transaction API in use
	 */
	public UnexpectedRollbackException(String msg, Throwable cause) {
		super(msg, cause);
	}

}

UnexpectedRollbackException继承了TransactionException,一般是事务嵌套,内层事务抛出了异常,外层事务给catch住了,然后试图提交事务报错

示例

    @Transactional
    public Customer createWithCatch(String name, String email) {
        Customer customer = customerRepository.save(new Customer(name, email));
        try {
            demoService.throwExInTx(0);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return customer;
    }

    // demoService
    @Transactional
    public void throwExInTx(int i) {
        int result = 1 / i;
    }

这里内层事务throwExInTx抛出了异常,而外层事务createWithCatch给catch住了,最后提交事务则报UnexpectedRollbackException异常

源码解析

invokeWithinTransaction

org/springframework/transaction/interceptor/TransactionAspectSupport.java

	protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
			final InvocationCallback invocation) throws Throwable {

		//......
		if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
			// Standard transaction demarcation with getTransaction and commit/rollback calls.
			TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

			Object retVal;
			try {
				// This is an around advice: Invoke the next interceptor in the chain.
				// This will normally result in a target object being invoked.
				retVal = invocation.proceedWithInvocation();
			}
			catch (Throwable ex) {
				// target invocation exception
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
				cleanupTransactionInfo(txInfo);
			}

			if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
				// Set rollback-only in case of Vavr failure matching our rollback rules...
				TransactionStatus status = txInfo.getTransactionStatus();
				if (status != null && txAttr != null) {
					retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
				}
			}

			commitTransactionAfterReturning(txInfo);
			return retVal;
		}
		//......	

	}		

TransactionAspectSupport的invokeWithinTransaction方法在执行invocation.proceedWithInvocation()时会catch住异常,然后执行completeTransactionAfterThrowing

completeTransactionAfterThrowing

org/springframework/transaction/interceptor/TransactionAspectSupport.java

	/**
	 * Handle a throwable, completing the transaction.
	 * We may commit or roll back, depending on the configuration.
	 * @param txInfo information about the current transaction
	 * @param ex throwable encountered
	 */
	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;
				}
			}
		}
	}

completeTransactionAfterThrowing会判断该异常是否要rollback,需要rollback的话,则执行txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus())

processRollback

org/springframework/transaction/support/AbstractPlatformTransactionManager.java

	/**
	 * Process an actual rollback.
	 * The completed flag has already been checked.
	 * @param status object representing the transaction
	 * @throws TransactionException in case of rollback failure
	 */
	private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
		try {
			boolean unexpectedRollback = unexpected;

			try {
				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 {
					// 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");
							}
							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) {
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
				throw ex;
			}

			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);
		}
	}

processRollback这里会判断,如果该事务外层还有事务,则判断status.isLocalRollbackOnly()或者是isGlobalRollbackOnParticipationFailure(默认返回true),然后执行doSetRollbackOnly(status)

doSetRollbackOnly

org/springframework/orm/jpa/JpaTransactionManager.java

	protected void doSetRollbackOnly(DefaultTransactionStatus status) {
		JpaTransactionObject txObject = (JpaTransactionObject) status.getTransaction();
		if (status.isDebug()) {
			logger.debug("Setting JPA transaction on EntityManager [" +
					txObject.getEntityManagerHolder().getEntityManager() + "] rollback-only");
		}
		txObject.setRollbackOnly();
	}

doSetRollbackOnly会获取txObject执行setRollbackOnly

setRollbackOnly

org/springframework/orm/jpa/JpaTransactionManager.java

		public void setRollbackOnly() {
			EntityTransaction tx = getEntityManagerHolder().getEntityManager().getTransaction();
			if (tx.isActive()) {
				tx.setRollbackOnly();
			}
			if (hasConnectionHolder()) {
				getConnectionHolder().setRollbackOnly();
			}
		}

setRollbackOnly这里会执行tx.setRollbackOnly()、getConnectionHolder().setRollbackOnly()标记为rollbackOnly

外层事务processCommit

org/springframework/transaction/support/AbstractPlatformTransactionManager.java

	/**
	 * Process an actual commit.
	 * Rollback-only flags have already been checked and applied.
	 * @param status object representing the transaction
	 * @throws TransactionException in case of commit failure
	 */
	private void processCommit(DefaultTransactionStatus status) throws TransactionException {
		try {
			boolean beforeCompletionInvoked = false;

			try {
				boolean unexpectedRollback = false;
				prepareForCommit(status);
				triggerBeforeCommit(status);
				triggerBeforeCompletion(status);
				beforeCompletionInvoked = true;

				if (status.hasSavepoint()) {
					if (status.isDebug()) {
						logger.debug("Releasing transaction savepoint");
					}
					unexpectedRollback = status.isGlobalRollbackOnly();
					status.releaseHeldSavepoint();
				}
				else if (status.isNewTransaction()) {
					if (status.isDebug()) {
						logger.debug("Initiating transaction commit");
					}
					unexpectedRollback = status.isGlobalRollbackOnly();
					doCommit(status);
				}
				else if (isFailEarlyOnGlobalRollbackOnly()) {
					unexpectedRollback = status.isGlobalRollbackOnly();
				}

				// Throw UnexpectedRollbackException if we have a global rollback-only
				// marker but still didn't get a corresponding exception from commit.
				if (unexpectedRollback) {
					throw new UnexpectedRollbackException(
							"Transaction silently rolled back because it has been marked as rollback-only");
				}
			}
			catch (UnexpectedRollbackException ex) {
				// can only be caused by doCommit
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
				throw ex;
			}
			catch (TransactionException ex) {
				// can only be caused by doCommit
				if (isRollbackOnCommitFailure()) {
					doRollbackOnCommitException(status, ex);
				}
				else {
					triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
				}
				throw ex;
			}
			catch (RuntimeException | Error ex) {
				if (!beforeCompletionInvoked) {
					triggerBeforeCompletion(status);
				}
				doRollbackOnCommitException(status, ex);
				throw ex;
			}

			// Trigger afterCommit callbacks, with an exception thrown there
			// propagated to callers but the transaction still considered as committed.
			try {
				triggerAfterCommit(status);
			}
			finally {
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
			}

		}
		finally {
			cleanupAfterCompletion(status);
		}
	}

processCommit在unexpectedRollback为true的时候会抛出UnexpectedRollbackException(Transaction silently rolled back because it has been marked as rollback-only);这里是status.isGlobalRollbackOnly()被标记为true了,因而unexpectedRollback为true

小结

UnexpectedRollbackException继承了TransactionException,一般是事务嵌套,内层事务抛出了异常,外层事务给catch住了,然后试图提交事务报错UnexpectedRollbackException(Transaction silently rolled back because it has been marked as rollback-only)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringSpring MVC都是Java Web应用程序开发框架。Spring是一个Java企业级应用程序开发框架,它提供了一个综合性的编程和配置模型,让开发人员可以轻松地创建各种类型的Java应用程序。Spring MVC是Spring框架的一部分,它是一个基于模型-视图-控制器(MVC)模式的Web应用程序开发框架,提供了一种灵活的方式来开发Web应用程序。 Spring主要提供了以下功能: 1. 控制反转(IoC):将对象的创建和依赖关系的管理从应用程序代码中解耦出来,使得应用程序更加灵活。 2. 面向切面编程(AOP):提供了一种能够在应用程序中针对特定点进行代码切入的机制,从而实现了诸如事务管理、日志记录和安全性控制等功能。 3. 数据访问和集成:提供了对JDBC、ORM和NoSQL数据存储等多种数据访问技术的支持,同时还提供了对各种企业级集成技术的支持。 4. Web开发:提供了Spring MVC框架,用于开发Web应用程序。 Spring MVC框架则提供了以下功能: 1. 轻量级Web框架:相比于其他Web框架,Spring MVC更加轻量级,易于使用。 2. 灵活的URL映射:Spring MVC提供了灵活的URL映射机制,可以根据不同的URL请求匹配不同的处理器。 3. 视图解析:Spring MVC支持多种视图技术,包括JSP、Freemarker、Velocity等。 4. 数据绑定:Spring MVC提供了一种方便的方式来将表单数据绑定到Java对象中,从而简化了表单处理的过程。 综上所述,SpringSpring MVC都是Java Web应用程序开发框架,它们提供了一系列功能来简化Java Web应用程序的开发。Spring提供了更广泛的功能,包括控制反转、面向切面编程、数据访问和集成等方面,而Spring MVC则是一个基于模型-视图-控制器模式的Web框架,提供了灵活的URL映射、视图解析和数据绑定等功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值