在spring事务中扩展业务操作;spring事务同步器TransactionSynchronizationManager

文章详细介绍了Spring框架中如何在事务提交后执行额外操作,主要涉及@TransactionalEventListener注解和TransactionSynchronizationManager类的使用。在事务同步管理中,重点讨论了事务状态和事务回调的执行顺序,以及不同事务传播级别在嵌套事务中的影响。特别指出,如果在事务回调中注册新的事务,应使用Propagation.REQUIRES_NEW以避免事务状态异常。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

业务上经常会有一些需求是需要在某个数据库操作事务提交之后再去操作。

我常用的就方式有@TransactionalEventListenerTransactionSynchronizationManager.

其实@TransactionalEventListener背后使用的也是TransactionSynchronizationManager

注意点:在afterCompletion方法中已经脱离当前事务了,如果想确保其中的方法也在一个事务中,那么就需要开启一个新事务.
在使用 >= spring 5.2是可以用org.springframework.transaction.support.TransactionTemplate#execute方法,里面会新开启一个事务。在小于上述版本时,可以手动开启事务来控制.

使用demo

//fallbackExecution = true代表没有事务的时候也监听
@TransactionalEventListener(value = {xxx.class}, fallbackExecution = true)
@Order(Ordered.HIGHEST_PRECEDENCE)
public void onApplicationEvent(xxx event) {

    log.info("监听成功,xxxxxx");
	//xxxxx

}

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCompletion(int status) {
                switch (status) {
                    case 0:
                        // 外部方法事务提交后执行的业务逻辑
                        break;
                    case 1:
                        // 外部方法事务回滚后执行的业务逻辑
                        break;
                    case 2:
                        // 外部方法事务异常时执行的业务逻辑
                        break;
                }
                
            }
        });

TransactionSynchronizationManager(事务同步器)分析

平时使用的时候都是直接用注解@Transactional,一般也知道是spring aop帮我们开启了事务,其中aop拦截使用事务注解的方法后使用的就是TransactionSynchronizationManager.

我用的是mybatis-spring-2.0.7,就从mybtais获取connection来入手了

org.mybatis.spring.transaction.SpringManagedTransaction#openConnection在从连接池获取connection的时候会判断是否开启了事务,如果已开启,那么会将连接暂时绑定到事务同步管理器中.

所谓的绑定其实就是塞到了一个threadLocal中和当前线程进行了一个关联。

业务中对事务的扩展方法通常都是在TransactionSynchronization的方法中执行的.

org.springframework.transaction.support.AbstractPlatformTransactionManager#triggerAfterCompletion中可以看到会将所有之前注册的同步事务操作按照List中的顺序(注册顺序)依次调用.

最后在org.springframework.transaction.support.AbstractPlatformTransactionManager#processCommit中的finally中可以看到对各项资源进行了释放,意味着我们仍旧可以在这之前拿到connection连接进行操作db

注意点:
如果在A事务中注册了一个同步事务,在同步事务回调中调用了一个B事务方法(B事务方法中又注册了另一个同步事务),要注意B事务的传播等级需要为Propagation.REQUIRES_NEW,不然后一个同步事务回调中的事务状态会变成TransactionSynchronization.STATUS_UNKNOWN.

debug了一下源码,发现在A事务提交完成后,在A事务的同步回调中的B事务还会加入A已经完成的事务中;

想到spring的事务是基于connection连接,而连接又是通过ThreadLocal和当前线程绑定(org.springframework.transaction.support.TransactionSynchronizationManager#doGetResource);
当A事务提交后,当前线程仍持有之前的连接,该连接在A事务完成之后并没有被重置,此时B事务时仍复用了当前连接;

导致B事务加入到了A事务中,但是此时A事务已经完成,B加入的是一个无效事务;
此时B事务中有一个同步事务回调在,按照默认的事务同步配置(AbstractPlatformTransactionManager.SYNCHRONIZATION_ALWAYS),会重新new一个事务状态,这个新new的事务状态导致在回调中会触发TransactionSynchronization.STATUS_COMMITTED;

最终导致B的事务同步回调中的状态不对;

综上所述,如果在注册事务同步方法之间有嵌套,最好使用PropagationREQUIRES_NEW传播方式

获取连接绑定connection给TransactionSynchronizationManager
在这里插入图片描述

TransactionSynchronization 扩展点

public interface TransactionSynchronization extends Ordered, Flushable {

    /** Completion status in case of proper commit. */
    // 事务提交状态
    int STATUS_COMMITTED = 0;

    /** Completion status in case of proper rollback. */
    // 事务回滚状态
    int STATUS_ROLLED_BACK = 1;

    /** Completion status in case of heuristic mixed completion or system errors. */
    // 事务异常状态
    int STATUS_UNKNOWN = 2;

    /**
     * 当前事务挂起时触发
     * Supposed to unbind resources from TransactionSynchronizationManager if managing any.
     * @see org.springframework.transaction.reactive.TransactionSynchronizationManager#unbindResource
     */
    default void suspend() {
    }

    /**
     * 当前事务恢复时触发(结束挂起时)
     * Supposed to rebind resources to TransactionSynchronizationManager if managing any.
     * @see org.springframework.transaction.reactive.TransactionSynchronizationManager#bindResource
     */
    default void resume() {
    }

    /**
     * 当前事务刷新时触发
     * 将基础会话刷新到数据存储区(如果适用),比如Hibernate/JPA的Session
     * @see org.springframework.transaction.TransactionStatus#flush
     */
    @Override
    default void flush() {
    }

    /**
     * 当前事务提交前触发,此处若发生异常,会导致回滚。
     * @see #beforeCompletion
     */
    default void beforeCommit(boolean readOnly) {
    }

    /**
     * 当前事务完成前触发。
     * 在beforeCommit之后,commit/rollback之前执行。即使异常,也不会回滚。
     * @see #beforeCommit
     * @see #afterCompletion
     */
    default void beforeCompletion() {
    }

    /**
     * 当前事务提交后触发
     */
    default void afterCommit() {
    }

    /**
     * 当前事务完成后触发
     * 通过status的value指定具体的触发时机(即上面的三个属性)
     */
    default void afterCompletion(int status) {
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值