事务:Springboot项目中对事务注解的应用

事务:Springboot项目中对事务注解的应用,第1张

事务:Springboot项目中对事务注解的应用
  • 前言
  • @Tranctional应用时的注意要点与说明
  • 注解属性介绍
  • 事务的回滚机制理解
    • 单类方法事务回滚机制
    • 多类事务的相互调用
  • Spring中事务的底层实现
    • 事务的基本执行原理
    • 事务的基本执行流程
    • 相关源码

前言

本文是对工作中应用到的事务注解进行经验总结,因为在事务注解上踩过的坑总是容易忘记,所以对事务注解进行总结~~~

事务,指的是访问并可能更新数据库中各种数据项的一个程序执行单元(unit),它是恢复和并发控制的基本单位,具有4个属性:原子性,一致性,隔离性以及持久性。在Spring框架中,事务管理具有丰富的功能支持,主要可以分为两种方式:

  1. 编程式事务: 允许用户在代码中精确定义事务的边界。编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。
  2. 声明式事务:基于AOP,有助于用户将 *** 作与事务规则进行解耦。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务管理也有两种常用的方式,一种是在配置文件(xml)中做相关的事务规则声明,另一种是基于@Transactional注解的方式。显然基于注解的方式更简单易用,更清爽。

在平时的业务中,考虑到代码的简洁性与业务的独立性,我们更多地往往会采用声明式事务管理,而最典型也是最简洁的就是采用事务注解@Transactional。

@Tranctional应用时的注意要点与说明
注意点注意事项说明
应用位置类,或者类中的某个方法上,接口,或者接口中某个方法建议不要添加到接口上,因为只有基于接口的代理时事务才会生效;建议不要添加到类上,而是类的方法上,这样方便后面其他非事务的函数开发
方法属性必须是public事务在private方法上不会生效,这是由Spring AOP本质决定的
事务开启方式基于接口代理或者基于类的实现类内部调用并不会引起事务行为,即使被调用方法有@Tranctional修饰
锁不能加在事务里面当出现并发情况时,可能存在第一个事务释放了锁,但还没来得及提交事务的时候第二个事务已经获取了锁,这样仍然无法避免并发问题
注解属性介绍
package org.springframework.transaction.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
    /**
     * 当在配置文件中有多个TransactionManager,可以使用该属性指定选择哪个事务管理器
     */
    @AliasFor("transactionManager")
    String value() default "";

    /**
     * 当在配置文件中有多个TransactionManager,可以使用该属性指定选择哪个事务管理器
     */
    @AliasFor("value")
    String transactionManager() default "";

    /**
     * 事务传播机制
     */
    Propagation propagation() default Propagation.REQUIRED;

    /**
     * 事务隔离机制
     */
    Isolation isolation() default Isolation.DEFAULT;

    /**
     * 超时时间,以s为单位
     */
    int timeout() default -1;

    /**
     * 事务读写性
     */
    boolean readOnly() default false;

    /**
     * 一组异常类,遇到时回滚
     */
    Class<? extends Throwable>[] rollbackFor() default {};

    /**
     * 一组异常类名,遇到时回滚
     */
    String[] rollbackForClassName() default {};

    /**
     * 一组异常类,遇到时不回滚
     */
    Class<? extends Throwable>[] noRollbackFor() default {};

    /**
     * 一组异常类名,遇到时不回滚
     */
    String[] noRollbackForClassName() default {};
}

在通常的业务中,我们关注的更多的是这些属性中针对事务所设置的不同回滚机制;

事务的回滚机制理解 单类方法事务回滚机制

默认Spring事务只发生在未被捕获的runtimeException即运行时异常或者是Error即错误时才启动回滚机制,它的捕获原理是:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚;

如果在事务中遇到异常,通常的解决方案主要有如下几种:

  1. 业务层处理事务,那么对方法不做异常捕获,或者在try…catch语句中最后加上 throw new runtimeException,这样会让aop捕获异常并做回滚,并且在业务层上一层去继续捕获异常并处理;
  2. 在try…catch语句中增加代码来进行手动回滚,这样上层即无需处理异常:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
  3. 对注解属性进行设置,令事务在遇到异常时能够自动回滚:
@Transactional(rollbackFor = Exception.class)
public void testTransationD() throws Exception{
    throw new Exception();
}
多类事务的相互调用

单个类方法的事务回滚容易理解,而不同类中的事务相互调用会根据不同的事务传播机制产生一点差异,例如:现在有两个类A,B,两个类中都各有一个带事务注解的方法aMethod,以及bMethod,在调用的时候不同的设置对应着不同的属性:

  1. 假设 aMethod 调用 bMethod,bMethod方法抛出异常,而aMethod没进行任务处理
public class AClass {

    private BClass bClass;

    @Autowired
    public void setbClass(BClass bClass) {
        this.bClass = bClass;
    }

    @Transactional(rollbackFor = Exception.class)
    public void aMethod() {
        //todo: 数据库 *** 作A(增,删,改)
        bClass.bMethod();
    }
}

public class BClass {

    @Transactional(rollbackFor = Exception.class)
    public void bMethod() {
        //todo: 数据库 *** 作A(增,删,改)
        throw new RuntimeException("函数执行有异常!");
    }}
}

  运行结果:A,B函数对数据库的 *** 作都回滚了。这是因为@Transaction注解默认的事务传播行为是:REQUIRED,它的含义是:如果当前没有事务,就应该新建一个事务,如果已经存在一个事务,则加入到这个事务中去。此时,对应的aMethod与bMethod属于同一个事务,则两者均回滚;

  1. 假设 aMethod 调用 bMethod,bMethod方法抛出异常,而aMethod函数中捕获了异常
public class AClass {

    private BClass bClass;

    @Autowired
    public void setbClass(BClass bClass) {
        this.bClass = bClass;
    }

    @Transactional(rollbackFor = Exception.class)
    public void aMethod() {
        //todo: 数据库 *** 作A(增,删,改)
        try {
            bClass.bMethod();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class BClass {

    @Transactional(rollbackFor = Exception.class)
    public void bMethod() {
        //todo: 数据库 *** 作A(增,删,改)
        throw new RuntimeException("函数执行有异常!");
    }}
}

  运行结果:A,B函数对数据库的 *** 作都 *** 作失败了并且抛出异常org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only;原因是:两个函数共用了一个事务,b抛出异常并调用了rollback函数,a未抛出异常,那么需要提交事务,会调用commit函数,这个时候之前被标记过了只能 rollback-only,直接会抛出异常;

   解决方法:我们可以将b方法上的事务注解增加属性:propagation = Propagation.REQUIRES_NEW,这样a,b是属于两个事务的,最终结果即是b回滚,a执行成功。

虽然事务的回滚机制强大又优秀,但需要明确的是,事务的回滚仅仅针对对数据库中表的 *** 作,针对程序中的变量,上传/下载文件等 *** 作,回滚是无效的;

Spring中事务的底层实现 事务的基本执行原理

事务是基于AOP机制进行实现的,一个Bean在执行Bean的创建生命周期时,会经过 InfrastructureAdvisorAutoProxyCreator 的初始化后的方法,会判断当前Bean对象是否和 BeanFactoryTransactionAttributeSourceAdvisor 匹配,匹配逻辑为判断该Bean的类上是否存在 @Transactional 注解,或者类中的某个方法上是否存在 `@Transactional 注解,如果存在则表示该Bean需要进行动态代理产生一个代理对象作为Bean对象。

事务的基本执行流程
  1. 利用所配置的PlatformTransactionManager事务管理器新建一个数据库连接;
  2. 修改数据库连接的autocommitfalse;
  3. 执行MethodInvocation.proceed()方法,简单理解就是执行业务方法,其中就会执行sql;
  4. 如果没有抛出异常,就提交,如果抛出异常,就回滚;
相关源码

TransactionInterceptor源码:

/**
* 源码位置:org.springframework.transaction.interceptor.TransactionInterceptor.invoke(MethodInvocation)
*/
public Object invoke(MethodInvocation invocation) throws Throwable {
    // 获取代理类
    Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

    // Adapt to TransactionAspectSupport's invokeWithinTransaction...
    return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
        @Override
        @Nullable
        public Object proceedWithInvocation() throws Throwable {
            // 执行后续的Interceptor,以及被代理的方法
            return invocation.proceed(); // test() sql
        }
        @Override
        public Object getTarget() {
            return invocation.getThis();
        }
        @Override
        public Object[] getArguments() {
            return invocation.getArguments();
        }
    });
}

/**
* 具体的执行代码
*/
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
        final InvocationCallback invocation) throws Throwable {

    // TransactionAttribute就是@Transactional中的配置
    TransactionAttributeSource tas = getTransactionAttributeSource();
    // 获取@Transactional注解中的属性值
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);

    // 返回Spring容器中类型为TransactionManager的Bean对象。事务的开启,提交,回滚都会用到TransactionManager对象。
    final TransactionManager tm = determineTransactionManager(txAttr);

    // ReactiveTransactionManager用得少,并且它只是执行方式是响应式的,原理流程和普通的是一样的
    if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
        boolean isSuspendingFunction = KotlinDetector.isSuspendingFunction(method);
        boolean hasSuspendingFlowReturnType = isSuspendingFunction &&
                COROUTINES_FLOW_CLASS_NAME.equals(new MethodParameter(method, -1).getParameterType().getName());
        if (isSuspendingFunction && !(invocation instanceof CoroutinesInvocationCallback)) {
            throw new IllegalStateException("Coroutines invocation not supported: " + method);
        }
        CoroutinesInvocationCallback corInv = (isSuspendingFunction ? (CoroutinesInvocationCallback) invocation : null);

        ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {
            Class<?> reactiveType =
                    (isSuspendingFunction ? (hasSuspendingFlowReturnType ? Flux.class : Mono.class) : method.getReturnType());
            ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(reactiveType);
            if (adapter == null) {
                throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " +
                        method.getReturnType());
            }
            return new ReactiveTransactionSupport(adapter);
        });

        InvocationCallback callback = invocation;
        if (corInv != null) {
            callback = () -> CoroutinesUtils.invokeSuspendingFunction(method, corInv.getTarget(), corInv.getArguments());
        }
        Object result = txSupport.invokeWithinTransaction(method, targetClass, callback, txAttr, (ReactiveTransactionManager) tm);
        if (corInv != null) {
            Publisher<?> pr = (Publisher<?>) result;
            return (hasSuspendingFlowReturnType ? KotlinDelegate.asFlow(pr) :
                    KotlinDelegate.awaitSingleOrNull(pr, corInv.getContinuation()));
        }
        return result;
    }

    // 把tm强制转换为PlatformTransactionManager,所以我们在定义时得定义PlatformTransactionManager类型
    PlatformTransactionManager ptm = asPlatformTransactionManager(tm);

    // joinpoint的唯一标识,就是当前在执行的方法名字
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

    // CallbackPreferringPlatformTransactionManager表示拥有回调功能的PlatformTransactionManager,也不常用
    if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
        // Standard transaction demarcation with getTransaction and commit/rollback calls.
        // 如果有必要就创建事务,这里就涉及到事务传播机制的实现了
        // TransactionInfo表示一个逻辑事务,比如两个逻辑事务属于同一个物理事务
        TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

        Object retVal;
        try {
            // 执行下一个Interceptor或被代理对象中的方法
            retVal = invocation.proceedWithInvocation(); //test
        }
        catch (Throwable ex) {
            // 抛异常了,则回滚事务
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            cleanupTransactionInfo(txInfo);
        }

        if (retVal != null && 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 {
        Object result;
        final ThrowableHolder throwableHolder = new ThrowableHolder();

        // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
        try {
            result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
                TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
                try {
                    Object retVal = invocation.proceedWithInvocation();
                    if (retVal != null && 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);
                }
            });
        }
        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;
        }


        // Check result state: It might indicate a Throwable to rethrow.
        if (throwableHolder.throwable != null) {
            throw throwableHolder.throwable;
        }
        return result;
    }
}

/**
* 创建事务的逻辑
*/
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
        @Nullable TransactionAttribute txAttr, final String joinpointIdentification) {

    // If no name specified, apply method identification as transaction name.
    // name为空,取方法名字
    if (txAttr != null && txAttr.getName() == null) {
        txAttr = new DelegatingTransactionAttribute(txAttr) {
            @Override
            public String getName() {
                return joinpointIdentification;
            }
        };
    }

    // 每个逻辑事务都会创建一个TransactionStatus,TransactionStatus中有一个属性代表当前逻辑事务底层的物理事务是不是新的
    TransactionStatus status = null;
    if (txAttr != null) {
        if (tm != null) {
            // 开启事务!
            status = tm.getTransaction(txAttr);
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
                        "] because no transaction manager has been configured");
            }
        }
    }

    // 返回一个TransactionInfo对象,表示得到了一个事务,可能是新创建的一个事务,也可能是拿到的已有的事务
    return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}

事务的提交核心逻辑:doCommit 函数,直接调用Connection的提交方法:

protected void doCommit(DefaultTransactionStatus status) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
    Connection con = txObject.getConnectionHolder().getConnection();
    if (status.isDebug()) {
        logger.debug("Committing JDBC transaction on Connection [" + con + "]");
    }
    try {
        con.commit();
    }
    catch (SQLException ex) {
        throw translateException("JDBC commit", ex);
    }
}

事务的回滚逻辑:

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

        // transactionAttribute的实现类为RuleBasedTransactionAttribute,父类为DefaultTransactionAttribute
        // 判断配置的rollBackFor的异常信息
        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;
            }
        }
    }
}

/**
* 判断回滚条件是否满足
* 源码位置:org.springframework.transaction.interceptor.RuleBasedTransactionAttribute.rollbackOn(Throwable)
*/
public boolean rollbackOn(Throwable ex) {
    RollbackRuleAttribute winner = null;
    int deepest = Integer.MAX_VALUE;

    if (this.rollbackRules != null) {
        // 遍历所有的RollbackRuleAttribute,判断现在抛出的异常ex是否匹配RollbackRuleAttribute中指定的异常类型的子类或本身
        for (RollbackRuleAttribute rule : this.rollbackRules) {
            int depth = rule.getDepth(ex);
            if (depth >= 0 && depth < deepest) {
                deepest = depth;
                winner = rule;
            }
        }
    }

    // User superclass behavior (rollback on unchecked) if no rule matches.
    // 没有匹配的规则,调用父类判断是不是运行时异常
    if (winner == null) {
        return super.rollbackOn(ex);
    }

    // ex所匹配的RollbackRuleAttribute,可能是NoRollbackRuleAttribute,如果是匹配的NoRollbackRuleAttribute,那就表示现在这个异常ex不用回滚
    return !(winner instanceof NoRollbackRuleAttribute);
}

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/918896.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-16
下一篇 2022-05-16

发表评论

登录后才能评论

评论列表(0条)

保存