• mysql 事务 及 Spring事务 初论


    1.事务概述

    事务是一种机制,用以维护数据库确保数据的完整性和一致性。。事务是用户定义的一个数据库操作序列,这些操作要么全做要么全不做,是一个不可分割的工作单位。例如,在关系数据库中,一个事务可以是一条SQL语句、一组SQL语句或整个程序。MySQL中主要使用INNODB存储引擎来支持事务
    在SQL语言中,定义事务的语句有三条:

    BEGIN | START  TRANSACTION  :开启事务
    COMMIT :提交事务
    ROLLBACK: 回滚事务
    
    • 1
    • 2
    • 3
    START TRANSACTION;
    
    UPDATE `tb_score`
    SET `score` = '91'
    WHERE
    	`id` = '1';
    
    COMMIT; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.事务的ACID特性:

    原子性A,一致性C,隔离性I,持久性D。
    原性性(Actomicity):事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行。
    一致性(Consistent):在事务开始和完成时,数据都必须保持一致状态。这意味着所有相关的数据规则都必须应用于事务的修改,以操持完整性;事务结束时,所有的内部数据结构(如B树索引或双向链表)也都必须是正确的。
    隔离性(Isolation):数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然。
    持久性(Durable):事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。

    3.事务的隔离级别:

    READ UNCOMMITTED:会出现脏读,不可重复读,幻读
    READ COMMITTED:会出现不可重复读,幻读,解决脏读
    REPEATABLE READ:会出现幻读(Mysql默认的隔离级别,但是Repeatable read配合gap锁不会出现幻读!) 解决不可重复读和解决脏读
    SERIALIZABLE:串行,避免以上的情况
    隔离粒度从上到下 加强。

    //查看当前事物级别:
    SELECT @@transaction_isolation;
    //设置mysql的隔离级别:
    set session transaction isolation level `设置事务隔离级别`
    eg:
    //设置read uncommitted级别:
    set session transaction isolation level read uncommitted;
    //设置read committed级别:
    set session transaction isolation level read committed;
    //设置repeatable read级别:
    set session transaction isolation level repeatable read;
    //设置serializable级别:
    set session transaction isolation level serializable;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    隔离级别不同引发的问题点:

    脏读(Dirty Reads):一个事务正在对一条记录做修改,在这个事务并提交前,这条记录的数据就处于不一致状态;这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”的数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象地叫做“脏读”。
    简述:一个事务读取到了因为一个事务未最终提交的变更过程中的数据。

    不可重复读(Non-Repeatable Reads):一个事务在读取某些数据已经发生了改变、或某些记录已经被删除了!这种现象叫做“不可重复读”。
    简述:一个事务修改了另一个事务未最终提交的读对应的数据。

    幻读(Phantom Reads):一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”。
    简述:一个事务A通过某些查询条件查出来了一些记录,在该事务未提交前,其他的事务写入或者删除了一些符合搜索条件的数据。导致事务A最终查询出的数据条数变多或者变少。

    小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。

    4.事务的ACID innodb底层的技术支持

    原子性A:主要是通过mysql 中的undo.log 回滚日志保障。比如对某一行数据进行了INSERT语句操作,那么undo log就记录一条与之相反的DELETE操作。主要用于事务的每个修改操作的逆操作。
    一致性C:通过原子性 持久性和隔离性最终保障。
    隔离性I:mysql 的各种锁和mvcc机制保障。
    持久性D: 通过mysql redo.log 重做日志保障。提供再写入操作,恢复提交事务修改的页操作,来保证事务的持久性

    注:
    redo log 和undo log都是引擎层(innodb)实现的日志。

    • undo log(回滚日志) 是Innodb 存储引擎层生成的日志 实现了事务中的原子性,主要用于事务回滚和 MVCC
    • redo log(重做日志) 是 Innodb 存储引擎层生成的日志,实现了事务中的持久性,主要用于故障恢复;

    4.1 Redo log
    MySQL 中的数据是存储在磁盘上的,但是如果每次读写数据都通过磁盘的话,读写的效率会非常低,所以 **InnoDB 在内存中设置了一个区域 Buffer Pool,可以直接通过内存来读取和修改数据,后续再将内存中的数据更新到磁盘中。**但是内存中的数据是易失性的,可能随着进程、系统崩溃等情况而丢失,所以 MySQL 设计了 redo log 来解决这类问题。
    当有一条记录需要更新的时候,InnoDB 引擎就会先更新内存(修改 Buffer Pool 中的数据页,同时标记为脏页),然后将本次对这个页的修改以 redo log 的形式记录下来(持久化到磁盘),这个时候更新就算完成了。后续,InnoDB 引擎会在适当的时候,由后台线程将缓存在 Buffer Pool 的脏页刷新到磁盘里,这就是 WAL (Write-Ahead Logging)技术。WAL 技术指的是, MySQL 的写操作并不是立刻写到磁盘上,而是先写日志,然后在合适的时间再写到磁盘上。

    优点:
    1.redo日志占用的空间非常小: 存储表空间ID、页号、偏移量以及需要更新的值,所需的存储空间是很小的,刷盘快
    2.redo日志是顺序写入磁盘的,顺序Io,效率比随机Io快。
    3.redo log的写入并不是直接写入磁盘的,InnoDB引擎会在写redo log的时候先写redo log buffer,之后以 一 定的频率 刷入到真正的redo log file 中。
    在这里插入图片描述

    4.2 Undo log
    在这里插入图片描述
    作用1:异常情况回滚数据,保证数据的原子性。
    作用2:MVCC 多版本并发控制里使用undo.log 中的roll_point回滚指针的属性来形成版本链。

    5.Spring 事务

    Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。

    5.1Spring的事务支持
    Spring 支持两种事务方式,分别是编程式事务声明式事务@Transactional。其中声明式事务是一个基于AOP的实现操作。
    但是项目中还是推荐编程式事务,虽然有代码入侵,但是性能可能更好,声明式事务虽然理论上优于编程式事务,注解声明事务简单,但也有不足,声明式事务管理的粒度是方法级别,而编程式事务是可以精确到代码块级别的。而且不注意@Transactional事务的失效场景的话,所加的事务并不会生效。

    编程式事务
    编程式事务是指将事务管理代码嵌入嵌入到业务代码中,TransactionTemplate 模板方法模式来控制事务的提交和回滚。下面的伪代码实现如下:

    @Autowired
    private TransactionTemplate transactionTemplate;
    
    public void testTransaction() {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                try {
                    // ....  业务代码
                } catch (Exception e){
                    //回滚
                    transactionStatus.setRollbackOnly();
                }
            }
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    声明式事务 @Transactional注解方式
    Spring声明式事务使用AOP 的环绕增强方式,在方法执行之前开启事务,在方法执行之后提交或回滚事务。对应的实现为 TransactionInterceptor ,其实现了 MethodInterceptor,即,通过AOP的环绕增强方式。
    底层更具体的源码 后续的博客中补充。
    TransactionInterceptor 事务拦截器 核心的 invoke(MethodInvocation invocation)方法。

    TransactionInterceptor 类继承了TransactionAspectSupport类。实现了方法器拦截接口MethodInterceptor。核心方法为invoke方法中调用的invokeWithinTransaction方法。

    public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
    
       /**
        * 无参构造方法
        * Create a new TransactionInterceptor.
        */
       public TransactionInterceptor() {
       }
    
       /**
        *有参构造方法
        * Create a new TransactionInterceptor.
        */
       public TransactionInterceptor(TransactionManager ptm, TransactionAttributeSource tas) {
          setTransactionManager(ptm);
          setTransactionAttributeSource(tas);
       }
    
       @Override
       @Nullable
       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 {
                return invocation.proceed();
             }
             @Override
             public Object getTarget() {
                return invocation.getThis();
             }
             @Override
             public Object[] getArguments() {
                return invocation.getArguments();
             }
          });
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    TransactionAspectSupport类中的核心事务增强方法invokeWithinTransaction,该方法中的createTransactionIfNecessary方法开启创建事务

    @Nullable
    protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
          final InvocationCallback invocation) throws Throwable {
    
       // If the transaction attribute is null, the method is non-transactional.
       //获取事务属性资源
       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) {
          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;
       }
    
       PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
       final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    
       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 {
             // This is an around advice: Invoke the next interceptor in the chain.
             // This will normally result in a target object being invoked.
             //around advice 。调用实际的业务逻辑方法
             retVal = invocation.proceedWithInvocation();
          }
          catch (Throwable ex) {
             // target invocation exception
             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;
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143

    createTransactionIfNecessary方法 开启创建事务,

    protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
          @Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
    
       // If no name specified, apply method identification as transaction name.
       if (txAttr != null && txAttr.getName() == null) {
          txAttr = new DelegatingTransactionAttribute(txAttr) {
             @Override
             public String getName() {
                return joinpointIdentification;
             }
          };
       }
    
       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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    TransactionManager.getTransaction(transactionAttribute)依据事务隔离级别开启事务

    @Override
    public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
          throws TransactionException {
    
       // Use defaults if no transaction definition given.
       TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
    
       Object transaction = doGetTransaction();
       boolean debugEnabled = logger.isDebugEnabled();
    
       if (isExistingTransaction(transaction)) {
          // Existing transaction found -> check propagation behavior to find out how to behave.
          return handleExistingTransaction(def, transaction, debugEnabled);
       }
    
       // Check definition settings for new transaction.
       if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
          throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
       }
    
       // 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'");
       }
       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;
          }
       }
       else {
          // Create "empty" transaction: no actual transaction, but potentially synchronization.
          if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
             logger.warn("Custom isolation level specified but no actual transaction initiated; " +
                   "isolation level will effectively be ignored: " + def);
          }
          boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
          return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
       }
    }
    /**
    *开启事务
    */
    private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
          boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {
    
       boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
       DefaultTransactionStatus status = newTransactionStatus(
             definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
        //核心方法
       doBegin(transaction, definition);
       prepareSynchronization(status, definition);
       return status;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    doBegin方法的不同实现。
    在这里插入图片描述
    DataSourceTransactionManager中的实现

    @Override
    protected void doBegin(Object transaction, TransactionDefinition definition) {
       DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
       Connection con = null;
    
       try {
          if (!txObject.hasConnectionHolder() ||
                txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
             Connection newCon = obtainDataSource().getConnection();
             if (logger.isDebugEnabled()) {
                logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
             }
             txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
          }
    
          txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
          con = txObject.getConnectionHolder().getConnection();
    
          Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
          txObject.setPreviousIsolationLevel(previousIsolationLevel);
          txObject.setReadOnly(definition.isReadOnly());
    
          // Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
          // so we don't want to do it unnecessarily (for example if we've explicitly
          // configured the connection pool to set it already).
          if (con.getAutoCommit()) {
             txObject.setMustRestoreAutoCommit(true);
             if (logger.isDebugEnabled()) {
                logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
             }
             con.setAutoCommit(false);
          }
    
          prepareTransactionalConnection(con, definition);
          txObject.getConnectionHolder().setTransactionActive(true);
    
          int timeout = determineTimeout(definition);
          if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
             txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
          }
    
          // Bind the connection holder to the thread.
          if (txObject.isNewConnectionHolder()) {
             TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
          }
       }
    
       catch (Throwable ex) {
          if (txObject.isNewConnectionHolder()) {
             DataSourceUtils.releaseConnection(con, obtainDataSource());
             txObject.setConnectionHolder(null, false);
          }
          throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    5.2 Spring事务传播机制
    所谓spring事务的传播属性,就是定义在存在多个事务同时存在的时候,spring应该如何处理这些事务的行为。这些属性在TransactionDefinition中定义。7种 默认为REQUIRED
    在这里插入图片描述
    当事务方法被另外一个事务方法调用时,必须指定事务应该如何传播,例如,方法可能继续在当前事务中执行,也可以开启一个新的事务,在自己的事务中执行。
    声明式事务的传播行为可以通过 @Transactional 注解中的 propagation 属性来定义,@Transactional 注解源代码如下:

    package org.springframework.transaction.annotation;
    
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @Documented
    public @interface Transactional {
    	@AliasFor("transactionManager")
    	String value() default "";
    	@AliasFor("value")
    	String transactionManager() default "";
    	String[] label() default {};
    	Propagation propagation() default Propagation.REQUIRED;
    	Isolation isolation() default Isolation.DEFAULT;
    	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
    	String timeoutString() default "";
    	boolean readOnly() default false;
    	Class<? extends Throwable>[] rollbackFor() default {};
    	String[] rollbackForClassName() default {};
    	Class<? extends Throwable>[] noRollbackFor() default {};
    	String[] noRollbackForClassName() default {};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    方法A是一个事务的方法,方法A执行过程中调用了方法B,那么方法B有无事务以及方法B对事务的要求不同都会对方法A的事务具体执行造成影响,同时方法A的事务对方法B的事务执行也有影响,这种影响具体是什么就由两个方法所定义的事务传播类型所决定。

    REQUIRED(Spring默认的事务传播类型 required:需要、依赖、依靠):如果当前没有事务,则自己新建一个事务,如果当前存在事务则加入这个事务。
    当A调用B的时候:如果A中没有事务,B中有事务,那么B会新建一个事务;如果A中也有事务、B中也有事务,那么B会加入到A中去,变成一个事务,这时,要么都成功,要么都失败。(假如A中有2SQLB中有2SQL,那么这四个SQL会变成一个SQL,要么都成功,要么都失败)
    
    SUPPORTS(supports:支持;拥护):当前存在事务,则加入当前事务,如果当前没有事务,就以非事务方法执行
    如果A中有事务,则B方法的事务加入A事务中,成为一个事务(一起成功,一起失败),如果A中没有事务,那么B就以非事务方式运行(执行完直接提交);
    
    MANDATORY(mandatory:强制性的):当前存在事务,则加入当前事务,如果当前事务不存在,则抛出异常。
    如果A中有事务,则B方法的事务加入A事务中,成为一个事务(一起成功,一起失败);如果A中没有事务,B中有事务,那么B就直接抛异常了,意思是B必须要支持回滚的事务中运行
    
    REQUIRES_NEW(requires_new:需要新建):创建一个新事务,如果存在当前事务,则挂起该事务。
    B会新建一个事务,AB事务互不干扰,他们出现问题回滚的时候,也都只回滚自己的事务;
    
    NOT_SUPPORTED(not supported:不支持):以非事务方式执行,如果当前存在事务,则挂起当前事务
    被调用者B会以非事务方式运行(直接提交),如果当前有事务,也就是A中有事务,A会被挂起(不执行,等待B执行完,返回);AB出现异常需要回滚,互不影响
    
    NEVER(never:从不): 如果当前没有事务存在,就以非事务方式执行;如果有,就抛出异常。就是B从不以事务方式运行
    A中不能有事务,如果没有,B就以非事务方式执行,如果A存在事务,那么直接抛异常
    
    NESTED(nested:嵌套的事务)嵌套事务:如果当前事务存在,则在嵌套事务中执行,否则REQUIRED的操作一样(开启一个事务)
    如果A中没有事务,那么B创建一个事务执行,如果A中也有事务,那么B会会把事务嵌套在里面。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    5.3 常见Spring事务失效场景
    a.数据表本身是不支持事务,导致事务失效
    如果使用MySQL且存储引擎是MyISAM,则事务是不起作用的,原因是MyIASM不支持事务。

    b.事务方法访问修饰符非public,是static、final,导致事务失效
    spring要求被代理方法必须是public的。源码里有写对代理的方法是不是public校验。

    如果事务是static、final的,同样无法通过动态代理,事务也是不会生效的。
      Spring的声明式事务是基于Aop 动态代理实现的,我们无法重写final修饰的方法;
      不管是JDK动态代理还是Cglib的动态代理,就是要通过代理的方式获取到代理的具体对象,而static方法修饰的方法是属于类的,不属于任何对象,所以static方法不能被重写,即便写法上是重写,但是并不具备重写的含义,也就是说static方法也不被进行动态代理。
      
    c. @Transactional注解所在的类没有被spring管理,导致事务失效
    加上@Service注解或者使用其他能注册成Spring Bean的方式或注解。

    d.catch掉异常之后,没有再次抛出异常,导致事务失效
    如果在加有事务的方法内,使用了try…catch…语句块对异常进行了捕获,而catch语句块没有throw new RuntimeException异常或者Spring支持的异常类型,则事务不会回滚。

    e.直接调用内部方法(this),导致事务失效
    非事务方法insert()中调用的自身类的事务方法insertUser()。spring采用动态代理机制来实现事务控制,而动态代理最终都是要调用原始对象的,而原始对象在去调用方法时,是不会再触发代理了!

    f.事务的传播特性设置错了,事务也会失效
    如下:propagation = Propagation.NEVER这种类型的传播特性不支持事务,如果有事务会抛出异常。
    目前只有这三种传播特性才会创建新事物:REQUIRED、REQUIRES_NEW、NESTED

  • 相关阅读:
    ChatGPT提示词工程(一):Guidelines准则
    优麒麟2204文件管理器peony扩展初开发
    Flow公链 |FCL1.0正式上线
    【安装笔记-20240529-Windows-Electerm 终端工具】
    PermissionX 1.6发布,支持Android 12,可能是今年最大的版本升级
    4、FFmpeg命令行操作6
    ctfshow MengXIn misc1
    基于Dlib训练自已的人脸数据集提高人脸识别的准确率
    点云从入门到精通技术详解100篇-双目鱼眼系统的畸变校正及目标重建
    Linux基础 - 服务管理(systemd)
  • 原文地址:https://blog.csdn.net/huangchong0107/article/details/136646283