mysql 事务 及 Spring事务 初论

1.事务概述

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

BEGIN | START  TRANSACTION  :开启事务
COMMIT :提交事务
ROLLBACK: 回滚事务
START TRANSACTION;

UPDATE `tb_score`
SET `score` = '91'
WHERE
	`id` = '1';

COMMIT; 

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;

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

脏读(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();
            }
        }
    });
}

声明式事务 @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();
         }
      });
   }
}

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

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

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

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

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

方法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会会把事务嵌套在里面。

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

  • 29
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值