SpringTx的使用和简单功能原理

362 阅读7分钟

SpringTx的使用和简单功能原理

基本操作

  1. 判断哪些动作是事务

  2. 参与已存在的或创建新的事务

  3. 完成

    • 提交

    • 回滚

透过代码看SQL

初始化连接线程时执行

/* mysql-connector-java-8.0.19 (Revision: a0ca826f5cdf51a98356fdfb1bf251eb042f80bf) */
SELECT  @@session.auto_increment_increment AS auto_increment_increment, @@character_set_client AS character_set_client, @@character_set_connection AS character_set_connection, @@character_set_results AS character_set_results, @@character_set_server AS character_set_server, @@collation_server AS collation_server, @@collation_connection AS collation_connection, @@init_connect AS init_connect, @@interactive_timeout AS interactive_timeout, @@license AS license, @@lower_case_table_names AS lower_case_table_names, @@max_allowed_packet AS max_allowed_packet, @@net_write_timeout AS net_write_timeout, @@performance_schema AS performance_schema, @@query_cache_size AS query_cache_size, @@query_cache_type AS query_cache_type, @@sql_mode AS sql_mode, @@system_time_zone AS system_time_zone, @@time_zone AS time_zone, @@tx_isolation AS transaction_isolation, @@wait_timeout AS wait_timeout
SET NAMES utf8mb4
SET character_set_results = NULL
SET autocommit=1
SET sql_mode='NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES'
SELECT @@session.tx_read_only
SELECT @@session.tx_isolation

执行带有事务的业务

SET autocommit=0 --关闭自动提交事务
insert into tx (tx_name) values ('random1651040479277') --从第一条语句执行时开启事务
select tx0_.id as id1_0_, tx0_.tx_name as tx_name2_0_ from tx tx0_ --平平无奇的查询语句
commit --提交事务
SET autocommit=1 --开启自动提交事务

执行非默认隔离级别的业务

SELECT @@session.tx_isolation --查询当前会话的隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED --不一样就设置
SET autocommit=0
insert into tx (tx_name) values ('random1651053787832')
select tx0_.id as id1_0_, tx0_.tx_name as tx_name2_0_ from tx tx0_
commit
SET autocommit=1
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ --执行后再改回去

设计的巧妙点:springtx并非采用我们熟知的begin或start transaction开启事务。而是使用autocommit=0来隐式开启事务。

事务的开启和关闭:

  1. 显式启动事务语句, begin 或 start transaction。配套的提交语句是 commit,回滚语句是 rollback。
  2. set autocommit=0,这个命令会将这个线程的自动提交关掉。意味着如果你只执行一个 select 语句,这个事务就启动了,而且并不会自动提交。这个事务持续存在直到你主动执行 commit 或 rollback 语句,或者断开连接。
  3. set autocommit=1,如果执行 commit work and chain,则是提交事务并自动启动下一个事务,这样也省去了再次执行 begin 语句的开销。

源码

声明式事务@Transactional

public @interface Transactional {

	@AliasFor("transactionManager")
	String value() default "";

	@AliasFor("value")
	String transactionManager() default "";

	Propagation propagation() default Propagation.REQUIRED; // 传播行为

	Isolation isolation() default Isolation.DEFAULT; // 隔离级别

	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT; // 超时机制,默认不超时

	boolean readOnly() default false; // 是否只读 

	Class<? extends Throwable>[] rollbackFor() default {};

	String[] rollbackForClassName() default {};

	Class<? extends Throwable>[] noRollbackFor() default {};

	String[] noRollbackForClassName() default {};

}

字段解读

  • value & transactionManager

    指定使用某个事务管理器TransactionManager,比如使用spring-data-jpa时默认是JpaTransactionManager

  • propagation

    事务的传播表现

    可选项

    • REQUIRED,默认,当前有事务则加入,没有则新建一个

      • 如果当前不存在事务则:
      @Override
      public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
          throws TransactionException {
      
          // ... 省略
      
          else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
                   def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
                   def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
              // 挂起当前事务
              SuspendedResourcesHolder suspendedResources = suspend(null);
              if (debugEnabled) {
                  logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
              }
              try {
                  // 开启一个新的事务
                  return startTransaction(def, transaction, debugEnabled, suspendedResources);
              }
              catch (RuntimeException | Error ex) {
                  resume(null, suspendedResources);
                  throw ex;
              }
          }
      }
      
      • 如果当前存在事务则:
      private TransactionStatus handleExistingTransaction(
          TransactionDefinition definition, Object transaction, boolean debugEnabled)
          throws TransactionException {
      
          // ... 省略
      
          // 如果能走到这步说明是REQUIRED或者SUPPORTS,那么
          // Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
          
          // ... 省略
          boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
          // 第二个参数表示当前事务,第三个参数false表示不开启事务
          return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
      }
      
    • REQUIRES_NEW,不管是否存在事务,都创建一个新的事务,原来的挂起(Suspend),新的执行完毕,继续执行老的事务

      • 如果当前不存在事务,同REQUIRED

      • 如果当前存在事务则:

      /**
      	 * Create a TransactionStatus for an existing transaction.
      	 */
      private TransactionStatus handleExistingTransaction(
          TransactionDefinition definition, Object transaction, boolean debugEnabled)
          throws TransactionException {
      
          // ...
      
          if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
              // 挂起当前事务
              SuspendedResourcesHolder suspendedResources = suspend(transaction);
              try {
                  // 开启新的事务
                  return startTransaction(definition, transaction, debugEnabled, suspendedResources);
              }
              catch (RuntimeException | Error beginEx) {
                  // ...
              }
          }
      }
      
    • NESTED,如果当前存在事务,则运行在一个嵌套的事务中,如果当前没有事务,则按照REQUIRED属性执行。某些DAO层框架不支持,比如JPA。

      • 如果当前不存在事务,同REQUIRED
      • 如果当前存在事务则:
      /**
      	 * Create a TransactionStatus for an existing transaction.
      	 */
      private TransactionStatus handleExistingTransaction(
          TransactionDefinition definition, Object transaction, boolean debugEnabled)
          throws TransactionException {
      
          // ...
      
          if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
              if (!isNestedTransactionAllowed()) {
                  throw new NestedTransactionNotSupportedException(
                      "Transaction manager does not allow nested transactions by default - " +
                      "specify 'nestedTransactionAllowed' property with value 'true'");
              }
              // 是否使用保存点来支持嵌套事务
              if (useSavepointForNestedTransaction()) {
                  // 使用jdbc3.0的保存点实现
                  // Create savepoint within existing Spring-managed transaction,
                  // through the SavepointManager API implemented by TransactionStatus.
                  // Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
                  DefaultTransactionStatus status =
                      prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
                  status.createAndHoldSavepoint();
                  return status;
              }
              else {
                  // Nested transaction through nested begin and commit/rollback calls.
                  // Usually only for JTA: Spring synchronization might get activated here
                  // in case of a pre-existing JTA transaction.
                  return startTransaction(definition, transaction, debugEnabled, null);
              }
          }
      }
      
    • SUPPORTS,若当前存在事务,则在事务中运行,否则以非事务形式运行

      private TransactionStatus handleExistingTransaction(
          TransactionDefinition definition, Object transaction, boolean debugEnabled)
          throws TransactionException {
      
          // ... 省略
      
          // 如果能走到这步说明是REQUIRED或者SUPPORTS,那么
          // Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
          
          // ... 省略
          boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
          // 第二个参数表示当前事务,第三个参数false表示不开启事务
          return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
      }
      
    • MANDATORY,必须在一个已有的事务中执行,否则抛出异常

      @Override
      public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
          throws TransactionException {
      
          // ... 省略
          
          // 如果存在事务则走这里后return
          if (isExistingTransaction(transaction)) {
              // Existing transaction found -> check propagation behavior to find out how to behave.
              return handleExistingTransaction(def, transaction, debugEnabled);
          }
      
          // 如果不存在事务走到了这里,但传播行为又是MANDATORY则抛异常
          // No existing transaction found -> check propagation behavior to find out how to proceed.
          if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
              throw new IllegalTransactionStateException(
                  "No existing transaction found for transaction marked with propagation 'mandatory'");
          }
      }
      
    • NOT_SUPPORTED,这个方法不开启事务

      private TransactionStatus handleExistingTransaction(
      			TransactionDefinition definition, Object transaction, boolean debugEnabled)
      			throws TransactionException {
      
          // ... 省略
          
          if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
              if (debugEnabled) {
                  logger.debug("Suspending current transaction");
              }
              // 如果现在有事务则挂起当前事务
              Object suspendedResources = suspend(transaction);
              // 是否起新的同步环境(其实TransactionSynchronizationManager就是一堆ThreadLocal做线程间数据同步)
              boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
              // 第三个入参newTransaction=false 表示不开启事务
              return prepareTransactionStatus(
                  definition, null, false, newSynchronization, debugEnabled, suspendedResources);
          }
      }
      
    • NEVER,不允许在事务内执行

      private TransactionStatus handleExistingTransaction(
          TransactionDefinition definition, Object transaction, boolean debugEnabled)
          throws TransactionException {
      
          if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
              throw new IllegalTransactionStateException(
                  "Existing transaction found for transaction marked with propagation 'never'");
          }
      }
      
  • isolation

    事务的隔离级别

    可选项

    • DEFAULT,默认,与数据库连接时读取数据库默认值SELECT @@session.tx_isolation or SELECT @@session.transaction_isolation
    • READ_UNCOMMITTED,读未提交
    • READ_COMMITTED,读已提交
    • REPEATABLE_READ,可重复度
    • SERIALIZABLE,串行化

    实现原理:如果业务想要的隔离级别与初始化时不一致则执行SQL设置

    SELECT @@session.tx_isolation --查询当前会话的隔离级别
    SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED --不一样就设置
    ...
    SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ --执行后再改回去
    
  • timeout

    事务超时时间,默认不超时(-1)。

    实现原理:将超时时间参数传递给DAO层框架,由DAO层框架自己实现判断,于执行SQL语句前判断事务是否超时。

  • readOnly

    是否是只读事务,如果是,但是又有增删改的话则抛出异常java.sql.SQLException: Connection is read-only

    实现原理:在事务执行前后执行SQL设置

    set session transaction read only
    ...
    set session transaction read write
    

事务事件监听@TransactionalEventListener

支持提交前提交后回滚后完成后(提交or回滚)

使用场景

案例1:事务提交后再做某种操作,比如发送MQ

案例2:事务回滚后对某些业务进行补偿

使用方法

@Slf4j
@Service
public class TransactionalTestService {

    @Autowired
    private ApplicationEventPublisher eventPublisher;

    @Transactional
    public void propWithListener() {
        System.out.println("start tx");
        eventPublisher.publishEvent(new TxAfterEvent("tx after event"));
        System.out.println("end tx");
    }

    @TransactionalEventListener
    public void listenEvent(TxAfterEvent txAfterEvent) {
        System.out.println(txAfterEvent);
    }

    public static class TxAfterEvent extends ApplicationEvent {
        public TxAfterEvent(Object source) {
            super(source);
        }
    }
}

结果

start tx
end tx
cn.cccyq.jpademo.service.TransactionalTestService$TxAfterEvent[source=tx after event]

事务管理器类图

image20220428140613521.png

基本事务管理类PlatformTransactionManager

public interface PlatformTransactionManager extends TransactionManager {
	/**
	 * 获取事务的状态信息(依据传播行为的不同,可能返回一个已激活的事务或创建一个新的独立事务)
	 */
	TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;

	/**
	 * 提交当前事务,依据事务的当前状态,也可能会进行回滚,比如标记为rollback-only的事务
	 */
	void commit(TransactionStatus status) throws TransactionException;

	/**
	 * 回滚当前事务,依据传播行为的不同,非独立的内部事务仅会打上rollback-only标记
	 */
	void rollback(TransactionStatus status) throws TransactionException;
}

AOP拦截类TransactionInterceptor

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
    @Override
	@Nullable
	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...
		return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
	}
}

AOP处理类TransactionAspectSupport

public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {
    @Nullable
	protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
			final InvocationCallback invocation) throws Throwable {
    }
}

事务定义类TransactionDefinition

用以定义事务的基本属性

事务数据共享类TransactionSynchronizationManager

用以实现同一个事务在不同类不同方法之间共享数据(其实就是一堆ThreadLocal)

参考

  1. SpringTx源码解析(一),anyoptional,anyoptional.com/2021/04/16/…
  2. SpringTx源码解析(二),anyoptional,www.jianshu.com/p/842d73955…
  3. Spring事务超时探讨,blog.csdn.net/m0_60449641…
  4. Spring事务注解@Transactional原理解析,zhuanlan.zhihu.com/p/141102728
  5. Spring 事务扩展机制 TransactionSynchronization,www.cnblogs.com/kkkfff/p/13…