Spring事务报错: org.springframework.transaction.UnexpectedRollbackException

异常信息:支持当前事务,如果不存在则抛出异常。事务被回滚,因为它被标记为仅回滚

org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only

	at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:873)
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:710)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:533)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:304)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)

Spring事务传播机制:可以查看如下枚举
总结如下:

  • PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务,就加入到这个事务中。默认策略
  • PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
  • PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
  • PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
  • PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
  • PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
    nested 的子异常抛出 如果被catch 外部插入成功 nested 的子异常抛出 如果不被catch 外部插入失败
    nested外面异常抛出 nest内部插入成功 nest也会跟着回滚
/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.transaction.annotation;

import org.springframework.transaction.TransactionDefinition;

/**
 * Enumeration that represents transaction propagation behaviors for use
 * with the {@link Transactional} annotation, corresponding to the
 * {@link TransactionDefinition} interface.
 *
 * @author Colin Sampaleanu
 * @author Juergen Hoeller
 * @since 1.2
 */
public enum Propagation {

	/**
	 * Support a current transaction, create a new one if none exists.
	 * Analogous to EJB transaction attribute of the same name.
	 * <p>This is the default setting of a transaction annotation.
	 */
	REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

	/**
	 * Support a current transaction, execute non-transactionally if none exists.
	 * 支持当前事务,如果不存在则以非事务方式执行。
	 * Analogous to EJB transaction attribute of the same name.
	 *  Note: For transaction managers with transaction synchronization,
	 * PROPAGATION_SUPPORTS is slightly different from no transaction at all,
	 * as it defines a transaction scope that synchronization will apply for.
	 * As a consequence, the same resources (JDBC Connection, Hibernate Session, etc)
	 * will be shared for the entire specified scope. Note that this depends on
	 * the actual synchronization configuration of the transaction manager.
	 * 注意:对于具有事务同步的事务管理器,

		PROPAGATION_SUPPORTS与完全没有事务略有不同

		因为它定义了同步将应用的事务范围

		因此,相同的资源(JDBC连接,Hibernate Sess

		将为整个指定范围共享。注意,这取决于

		事务管理器的实际同步配置。
	 * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization
	 */
	SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

	/**
	 * Support a current transaction, throw an exception if none exists.
	 * 支持当前事务,如果不存在则抛出异常。
	 * Analogous to EJB transaction attribute of the same name.
	 */
	MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

	/**
	 * Create a new transaction, and suspend the current transaction if one exists.
	 * 创建一个新事务,如果存在当前事务,则挂起当前事务。
	 * Analogous to the EJB transaction attribute of the same name.
	 * <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
	 * on all transaction managers. This in particular applies to
	 * {@link org.springframework.transaction.jta.JtaTransactionManager},
	 * which requires the {@code javax.transaction.TransactionManager} to be
	 * made available to it (which is server-specific in standard Java EE).
	 * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
	 */
	REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),

	/**
	 * Execute non-transactionally, suspend the current transaction if one exists.
	 * 以非事务方式执行,如果存在当前事务,则暂停当前事务。
	 * Analogous to EJB transaction attribute of the same name.
	 * <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
	 * on all transaction managers. This in particular applies to
	 * {@link org.springframework.transaction.jta.JtaTransactionManager},
	 * which requires the {@code javax.transaction.TransactionManager} to be
	 * made available to it (which is server-specific in standard Java EE).
	 * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
	 */
	NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

	/**
	 * Execute non-transactionally, throw an exception if a transaction exists.
	 * 以非事务方式执行,如果存在事务则抛出异常。
	 * Analogous to EJB transaction attribute of the same name.
	 */
	NEVER(TransactionDefinition.PROPAGATION_NEVER),

	/**
	 * Execute within a nested transaction if a current transaction exists,
	 * 如果当前事务存在,则在嵌套事务中执行,
	 * behave like PROPAGATION_REQUIRED else. There is no analogous feature in EJB.
	 * 行为类似于PROPAGATION_REQUIRED else。EJB中没有类似的特性。
	 * <p>Note: Actual creation of a nested transaction will only work on specific
	 * transaction managers. Out of the box, this only applies to the JDBC
	 * DataSourceTransactionManager when working on a JDBC 3.0 driver.
	 * Some JTA providers might support nested transactions as well.
	 * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
	 */
	NESTED(TransactionDefinition.PROPAGATION_NESTED);


	private final int value;


	Propagation(int value) {
		this.value = value;
	}

	public int value() {
		return this.value;
	}

}

spring事务默认的传播行为:
@Transactional 等于 @Transactional(propagation=Propagation.REQUIRED)

发生异常的场景描述:
在使用Spring事务时,在一个事务A中又开了一个事务B(即存在嵌套事务),当事务B发生异常时,将异常catch后事务B会进行回滚操作,若此时异常被直接吃掉(即事务A无法得知发生过异常),则事务A会抛出如上异常。

serviceA有事务 @Transactional
 serviceB有事务 @Transactional
 
有@Transactional
	ServiceA -> {
	    ServiceA
     try {
            @Transactional()
             ServiceB{
              异常 BusinessException
              事务回滚
             }
        } catch (Exception e) {
        }
	} 

原因:
因为ServiceB的传播属性设置为PROPAGATION_REQUIRED,PROPAGATION_REQUIRED的意思是,当前有事务,则使用当前事务,当前无事务则创建事务。由于ServiceA的传播属性也为PROPAGATION_REQUIRED,所以ServiceA会创建一个事务,然后ServiceB与ServiceA使用同一个事务,ServiceB出现异常后,将当前事务标志位回滚,由于在ServiceA中做了trycatch处理,程序没有终止而是继续往下走,当事务commit时,check状态,发现,需要事务回滚,所以才会出现不可预知的事务异常:因为事务被标志位回滚,所以事务回滚。
也就是说:ServiceA与ServiceB共用一个事务,ServiceB将事务标志为回滚,ServiceA中commit这个事务,然后,出现事务已经被标志回滚(ServiceB标志的)的异常信息。

解决方案
情况1:ServiceA与ServiceB在逻辑上不应该属于同一个事务,那么将ServiceB的事务传播属性修改为PROPAGATION_REQUIRES_NEW,这样,执行ServiceB时,会创建一个新的事务,不影响ServiceA中的事务。

情况2:业务A与业务B在业务逻辑上就应该属于同一个事务,那么将ServiceA中的try catch去掉

情况3:业务A与业务B在业务逻辑上就应该属于同一个事务,但是ServiceB的失败与否不能影响ServiceA的事务提交,那么仍然在ServiceA中try catch ServiceB,并将ServiceB设置为PROPAGATION_NESTED,它的意思是,ServiceB是一个子事务,有一个savepoint,失败时会回滚到savepoint,不影响ServiceA,如果成功则A、B一起提交,A与B都是一个事务,只是B是一个子事务。
情况4:业务A 有无事务无影响 去掉业务A的@Transactional

猜你喜欢

转载自blog.csdn.net/qq_34041723/article/details/129593079