spring事务

事务介绍

事务:一组只能同时成功或失败的执行命令。
事务特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)
隔离级别:

  • READ UNCOMMITTED (读未提交)SELECT语句以非锁定方式执行,但可能使用行的早期版本。因此,使用这个隔离级别,这样的读取是不一致的。这也叫脏读。
  • READ COMMITTED(读已提交)即使在同一事务中,每个一致的读取都将设置并读取其自己的新快照。
  • REPEATABLE READ (可重复读)这是InnoDB得默认隔离级别。 同一事务中的一致读取将读取第一次读取建立的 快照。这意味着,如果您SELECT 在“同一事务”中发出多个普通(非锁定)语句,则这些 SELECT语句彼此之间也是一致的。
  • SERIALIZABLE (序列化)此级别类似于REPEATABLE READ,但如果禁用了自动提交,则InnoDB会将所有普通的SELECT语句隐式转换为SELECT ... FOR SHARE。 如果启用了自动提交,则SELECT是它自己的事务。 因此,它被认为是只读的,并且如果以一致的(非锁定)读取方式执行并且不需要阻塞其他事务就可以序列化。 (如果其他事务已修改选定的行,则强制普通SELECT阻止,请禁用自动提交。)
spring事务

spring事务是通过AOP代理启用此支持,并且事务建议由元数据(当前基于XML或基于注释)驱动。 AOP与事务性元数据(即被aop增强的事物)的组合产生了一个AOP代理,该代理将TransactionInterceptor(事务拦截器)与适当的TransactionManager(事务管理器)实现结合使用,以驱动方法调用周围的事务。

Spring的TransactionInterceptor为命令式和反应式编程模型提供事务管理。 拦截器通过检查方法返回类型来检测所需的事务管理风格。 返回反应性类型(例如Publisher或Kotlin Flow(或其子类型))的方法符合反应式事务管理的条件。 所有其他返回类型(包括void)都将代码路径用于命令式事务管理。

同时不同的事务管理风格需要不同的事务管理器,命令式事务需要PlatformTransactionManager,而反应式事务则使用ReactiveTransactionManager实现,TransactionManager在不同的环境中有不同的实现,如PlatformTransactionManager(纯jdbc)、JtaTransactionManager(JTA)、HibernateTransactionManager(Hibernate)等。

spring事务执行流程

TransactionDefinition接口指定:

  • 传播行为:通常,事务范围内的所有代码都在该事务中运行。但是,如果在已存在事务上下文的情况下运行事务方法,则可以指定行为。例如,代码可以在现有事务中继续运行(常见情况),或者可以暂停现有事务并创建新事务。 Spring提供了EJB CMT熟悉的所有事务传播选项。要了解有关Spring中事务传播的语义的信息,请参阅事务传播。

  • 隔离级别:此事务与其他事务的工作隔离的程度。例如,该交易能否看到其他交易的未提交写入?

  • 超时回滚:超时之前该事务运行了多长时间,并被基础事务基础结构自动回滚。

  • 只读状态:当代码读取但不修改数据时,可以使用只读事务。在某些情况下,例如使用Hibernate时,只读事务可能是有用的优化。

  • 回滚规则:指定哪些异常(和可抛出对象)应引起自动回滚。

<tx:advice />标记指定的各种事务设置。 默认的<tx:advice />设置为:

  • 传播行为 REQUIRED。
  • 隔离级别 DEFAULT。
  • 事务只读状态 false(读写)。
  • 事务超时 默认为基础事务系统的默认超时,如果不支持超时,则默认为无。
  • 回滚规则 任何RuntimeException都会触发回滚,而任何checkedException都不会触发。
Attribute Required Default Description
name Yes 与事务属性关联的方法名称。 通配符(*)可用于将相同的事务属性设置与多种方法相关联(例如,get *,handle *,on * Event等)。
propagation No REQUIRED 事务传播行为。
isolation No DEFAULT 事务隔离级别。 仅适用于传播设置REQUIRED或REQUIRES_NEW。
timeout No -1 事务超时(秒)。 仅适用于REQUIRED或REQUIRES_NEW传播。
read-only No false 读写与只读事务。 仅适用于REQUIRED或REQUIRES_NEW。
rollback-for No 逗号分隔的触发回滚的Exception实例列表。 例如,com.foo.MyBusinessException,ServletException。
no-rollback-for No 不触发回滚的Exception实例的逗号分隔列表。 例如,com.foo.MyBusinessException,ServletException。

事务传播行为(org.springframework.transaction.annotation.Propagation):

  • REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED) 支持当前事务,如果不存在则创建新事务。*类似于同名的EJB事务属性。默认设置。
  • SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS) 支持当前事务,如果不存在,则以非事务方式执行。
  • MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY) 支持当前事务,如果不存在则抛出异常。
  • REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW) 创建新事务,如果当前事务存在,则挂起当前事务。尤其适用于JtaTransactionManager
  • NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED) 以非事务方式执行,如果存在当前事务,则挂起当前事务。尤其适用于JtaTransactionManager。
  • NEVER(TransactionDefinition.PROPAGATION_NEVER) 以非事务方式执行,如果存在事务,则引发异常。
  • NESTED(TransactionDefinition.PROPAGATION_NESTED) 如果当前事务存在,则在嵌套事务中执行,否则的行为类似于REQUIRED。
xml配置方式

基础工程 https://www.cnblogs.com/jinit/p/13912571.html
application.xml

<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                            http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                            http://www.springframework.org/schema/tx
                            https://www.springframework.org/schema/tx/spring-tx.xsd
                            http://www.springframework.org/schema/aop
                            https://www.springframework.org/schema/aop/spring-aop.xsd
                            http://www.springframework.org/schema/context
                            http://www.springframework.org/schema/context/spring-context-4.1.xsd">

    <!--组件扫描-->
    <context:component-scan base-package="tx.*"/>
    <context:property-placeholder location="classpath:jdbc-config.properties" ignore-unresolvable="true"/>
    <!--详细配置参数见 com.alibaba.druid.pool.DruidAbstractDataSource-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="initialSize" value="1"/>
        <property name="minIdle" value="1"/>
        <property name="maxActive" value="20"/>
        <property name="maxWait" value="60000"/>
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
    </bean>
    <!--配置sqlSessionFactoryBean对象-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <!--mybatis核心配置文件-->
        <property name="configLocation" value="classpath:mybatis.xml"/>
    </bean>
    <!--配置事务管理器-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--配置aop通知-->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <!-- the transactional semantics... -->
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
    <!--aop编程配置,配置切入点(即对该方法进行增强),任意类型返回值 tx包下,OrderService的所有方法-->
    <!--<aop:config>
        <aop:pointcut id="orderServiceOperation" expression="execution(* tx.OrderService.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="orderServiceOperation"/>
    </aop:config>-->
    <!--配置Mapper对象-->
    <bean class="org.mybatis.spring.mapper.MapperFactoryBean" scope="singleton">
        <property name="mapperInterface" value="tx.mapper.AccountMapper"/>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperFactoryBean" scope="singleton">
        <property name="mapperInterface" value="tx.mapper.OrderMapper"/>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperFactoryBean" scope="singleton">
        <property name="mapperInterface" value="tx.mapper.StorageMapper"/>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

    <!--注册OrderService bean-->
    <bean id="orderService" class="tx.OrderService"/>
</beans>

OrderService.java

package tx;

import org.springframework.beans.factory.annotation.Autowired;
import tx.entity.*;
import tx.mapper.*;
/**
 * @author :jty
 * @date :20-10-30
 */
public class OrderService {
    @Autowired
    OrderMapper orderMapper;
    @Autowired
    StorageMapper storageMapper;
    @Autowired
    AccountMapper accountMapper;
   //去掉注释使用xml
   // @Transactional(rollbackFor = Exception.class)
    public void doOrder(){
        //bz0001库存减20
        storageMapper.update(new Storage(null,"bz0001",100-20,100));
        //增加20份订单
        orderMapper.add(
                new Order(null,0001,"bz0001",20,20*100));
        //手动制造一个异常,观察数据库变化
        int m=1/0;

        accountMapper.update(new Account(null,0001,2000-20*100));
    }
}

  • 库存减少storageMapper.update(new Storage(null,"bz0001",100-20,100));

  • 订单增加orderMapper.add(new Order(null,0001,"bz0001",20,20*100));

  • 账户金额不变accountMapper.update(new Account(null,0001,2000-20*100));,被异常中断

  • 将aop配置注释放开后观察到数据库不发生改变(重置数据库后执行代码)

 <!--<aop:config>
        <aop:pointcut id="orderServiceOperation" expression="execution(* tx.OrderService.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="orderServiceOperation"/>
    </aop:config>-->
为不同的bean配置不同的事务
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 配置不同的切入点 -->
    <aop:config>

        <aop:pointcut id="defaultServiceOperation"
                expression="execution(* x.y.service.*Service.*(..))"/>

        <aop:pointcut id="noTxServiceOperation"
                expression="execution(* x.y.service.ddl.DefaultDdlManager.*(..))"/>

        <aop:advisor pointcut-ref="defaultServiceOperation" advice-ref="defaultTxAdvice"/>

        <aop:advisor pointcut-ref="noTxServiceOperation" advice-ref="noTxAdvice"/>

    </aop:config>

    <!-- this bean will be transactional (see the 'defaultServiceOperation' pointcut) -->
    <bean id="fooService" class="x.y.service.DefaultFooService"/>

    <!-- this bean will also be transactional, but with totally different transactional settings -->
    <bean id="anotherFooService" class="x.y.service.ddl.DefaultDdlManager"/>

    <!-- 不同的通知 -->
    <tx:advice id="defaultTxAdvice">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <tx:advice id="noTxAdvice">
        <tx:attributes>
            <tx:method name="*" propagation="NEVER"/>
        </tx:attributes>
    </tx:advice>

    <!-- 其他配置同单个(上述) -->

</beans>
使用@Transactional

注释在上面的类级别使用,注释指示声明类(及其子类)的所有方法的默认值。 另外,每种方法都可以单独注释。 请注意,类级别的注释不适用于类层次结构中的祖先类。 在这种情况下,需要在本地重新声明方法,以参与子类级别的注释。

  • 应仅将@Transactional注释应用于具有公共可见性的方法。如果使用注释对受保护的,私有的或程序包可见的方法进行@Transactional注释,则不会引发任何错误,但是带注释的方法不会显示已配置的事务设置。
  • 建议使用@Transactional注释仅注释具体的类(以及具体类的方法),而不是注释接口。 您当然可以在接口(或接口方法)上放置@Transactional批注,但这仅在您使用基于接口的代理时才可以正常使用。 Java注释不是从接口继承的事实意味着,如果您使用基于类的代理(proxy-target-class="true")或基于编织的方面(mode="aspectj"),则事务设置不会 由代理和编织基础结构识别,并且该对象未包装在事务代理中。
    application.xml
<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                            http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                            http://www.springframework.org/schema/tx
                            https://www.springframework.org/schema/tx/spring-tx.xsd
                            http://www.springframework.org/schema/aop
                            https://www.springframework.org/schema/aop/spring-aop.xsd
                            http://www.springframework.org/schema/context
                            http://www.springframework.org/schema/context/spring-context-4.1.xsd">

    <!--组件扫描-->
    <context:component-scan base-package="tx.*"/>
    <context:property-placeholder location="classpath:jdbc-config.properties" ignore-unresolvable="true"/>
    <!--详细配置参数见 com.alibaba.druid.pool.DruidAbstractDataSource-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="initialSize" value="1"/>
        <property name="minIdle" value="1"/>
        <property name="maxActive" value="20"/>
        <property name="maxWait" value="60000"/>
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
    </bean>
    <!--配置sqlSessionFactoryBean对象-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <!--mybatis核心配置文件-->
        <property name="configLocation" value="classpath:mybatis.xml"/>
    </bean>
    <!--配置事务管理器-->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- enable the configuration of transactional behavior based on annotations 开启事务注解-->
    <!-- 去掉tx:advice和aop:config配置,通过@Transactional注解定义事务和定位aop代理目标-->
    <tx:annotation-driven transaction-manager="txManager"/><!-- a TransactionManager is still required -->
    <!--注释掉tx:advice和aop:config-->
    <!--配置aop通知-->
    <!--<tx:advice id="txAdvice" transaction-manager="txManager">
        &lt;!&ndash; the transactional semantics... &ndash;&gt;
        <tx:attributes>
            &lt;!&ndash;get方法事务只读&ndash;&gt;
            <tx:method name="get*" read-only="true" isolation="DEFAULT" timeout="-1" rollback-for="RuntimeException" propagation="REQUIRED" no-rollback-for="ArithmeticException"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>-->
    <!--aop编程配置,配置切入点(即对该方法进行增强),任意类型返回值 tx包下,OrderService的所有方法-->
   <!-- <aop:config>
        <aop:pointcut id="orderServiceOperation" expression="execution(* tx.OrderService.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="orderServiceOperation"/>
    </aop:config>-->
    <!--配置Mapper对象-->
    <bean class="org.mybatis.spring.mapper.MapperFactoryBean" scope="singleton">
        <property name="mapperInterface" value="tx.mapper.AccountMapper"/>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperFactoryBean" scope="singleton">
        <property name="mapperInterface" value="tx.mapper.OrderMapper"/>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
    <bean class="org.mybatis.spring.mapper.MapperFactoryBean" scope="singleton">
        <property name="mapperInterface" value="tx.mapper.StorageMapper"/>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

    <!--注册OrderService bean-->
    <bean id="orderService" class="tx.OrderService"/>
</beans>

OrderService.java

public class OrderService {
    @Autowired
    OrderMapper orderMapper;
    @Autowired
    StorageMapper storageMapper;
    @Autowired
    AccountMapper accountMapper;

   @Transactional(rollbackFor = Exception.class,propagation= Propagation.REQUIRED,isolation= Isolation.DEFAULT,timeout = -1)
    public void doOrder(){
        //bz0001库存减20
        storageMapper.update(new Storage(null,"bz0001",100-20,100));
        //增加20份订单
        orderMapper.add(
                new Order(null,0001,"bz0001",20,20*100));
        //手动制造一个异常,观察数据库变化
        int m=1/0;

        accountMapper.update(new Account(null,0001,1500-20*100));
    }
}
原理
   <!--注册OrderService bean-->
    <bean id="orderService" class="tx.OrderService"/>

  <tx:advice id="txAdvice" transaction-manager="txManager">
        <!-- the transactional semantics... -->
        <tx:attributes>
            <!--get方法事务只读-->
            <tx:method name="get*" read-only="true" isolation="DEFAULT" timeout="-1" rollback-for="RuntimeException" propagation="REQUIRED" no-rollback-for="ArithmeticException"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
  <aop:config>
        <aop:pointcut id="orderServiceOperation" expression="execution(* tx.OrderService.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="orderServiceOperation"/>
    </aop:config>
  • 通过tx:advice标签可知道该标签配置的对象为org.springframework.transaction.interceptor.TransactionInterceptor
  • 通过aop:config标签可知道spring通过txAdvice对象的invoke(MethodInvocation invocation)方法代理orderServiceOperation对象
  • 即通过该方法org.springframework.transaction.interceptor.TransactionInterceptor#invoke代理我们定义的切入点
//方法调用的描述,在方法调用时提供给拦截器。即在调用切入掉表达式式定义的方式时,将被拦截获得该对象MethodInvocation
public Object invoke(MethodInvocation invocation) throws Throwable {
		// Work out the target class: may be {@code null}.
		// The TransactionAttributeSource should be passed the target class
		// as well as the method, which may be from an interface.
                //我们注入的连接点的类,通过切入点表达式获取
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

		 // Adapt to TransactionAspectSupport's invokeWithinTransaction...
                 //invocation.getMethod():tx.OrderService.doOrder(),被代理的方法;invocation::proceed:invocation的proceed方法作为参数,
                 //‘::’方法引用,接受该方法的接口
    /*@FunctionalInterface
    protected interface InvocationCallback {

    Object proceedWithInvocation() throws Throwable;
    }*/
                 //即InvocationCallback.proceedWithInvocation()相当于invocation.proceed();
                 //旧版本代码如下
    /* return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
    @Override
    public Object proceedWithInvocation() throws Throwable {
        return invocation.proceed();
    }
    });*/
		return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
	}

TransactionAspectSupport.invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,final InvocationCallback invocation)

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

		// If the transaction attribute is null, the method is non-transactional.
                //获取 <tx:attributes>中的配置
		TransactionAttributeSource tas = getTransactionAttributeSource();
                //获取事务定义参数
		final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
                //确定要用于给定事务的特定事务管理器。
		final TransactionManager tm = determineTransactionManager(txAttr);
                //反应式事务
		if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
			ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {
				if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
					throw new TransactionUsageException(
							"Unsupported annotated transaction on suspending function detected: " + method +
							". Use TransactionalOperator.transactional extensions instead.");
				}
				ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(method.getReturnType());
				if (adapter == null) {
					throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " +
							method.getReturnType());
				}
				return new ReactiveTransactionSupport(adapter);
			});
			return txSupport.invokeWithinTransaction(
					method, targetClass, invocation, txAttr, (ReactiveTransactionManager) tm);
		}
                //非反应式事务
		PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
                //获取连接点 tx.OrderService.doOrder
		final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

		if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
			// Standard transaction demarcation with getTransaction and commit/rollback calls.
                        //使用getTransaction和commit / rollback调用进行标准事务划分。
                        //获取事务信息 包括事务管理器、连接点、事务定义参数、事务状态等
			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;
		}

		else {
			final ThrowableHolder throwableHolder = new ThrowableHolder();

			// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
			try {
				Object result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
					TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
					try {
						Object retVal = invocation.proceedWithInvocation();
						if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
							// Set rollback-only in case of Vavr failure matching our rollback rules...
							retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
						}
						return retVal;
					}
					catch (Throwable ex) {
						if (txAttr.rollbackOn(ex)) {
							// A RuntimeException: will lead to a rollback.
							if (ex instanceof RuntimeException) {
								throw (RuntimeException) ex;
							}
							else {
								throw new ThrowableHolderException(ex);
							}
						}
						else {
							// A normal return value: will lead to a commit.
							throwableHolder.throwable = ex;
							return null;
						}
					}
					finally {
						cleanupTransactionInfo(txInfo);
					}
				});

				// Check result state: It might indicate a Throwable to rethrow.
				if (throwableHolder.throwable != null) {
					throw throwableHolder.throwable;
				}
				return result;
			}
			catch (ThrowableHolderException ex) {
				throw ex.getCause();
			}
			catch (TransactionSystemException ex2) {
				if (throwableHolder.throwable != null) {
					logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
					ex2.initApplicationException(throwableHolder.throwable);
				}
				throw ex2;
			}
			catch (Throwable ex2) {
				if (throwableHolder.throwable != null) {
					logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
				}
				throw ex2;
			}
		}
	}

posted @ 2020-11-03 10:11  复一日  阅读(151)  评论(0编辑  收藏  举报