Spring源码之@Transactional源码解析二

目录

一、事务的传播属性

一、传播属性的概述

二、Propagation.REQUIRED属性

            ①、实例代码:

            ②、Debug调试

            ③、正常情况伪代码流程

            ④、代码添加异常

            ⑤、经典案例一

            ​​​​​​⑦、分析:

            ⑧、经典案例二 

            ⑨、伪代码案列二 

            ⑩、分析二 

二、Propagation.REQUIRED_NEW属性

             ①、实例代码:

             ②、源码跟读:

二、Propagation.REQUIRED_NSTED属性

             ①、示例代码:

             ②、核心源码

二、总结:


一、事务的传播属性

一、传播属性的概述

  1. Propagation.REQUIRED(required):支持当前事务,如果当前有事务, 那么加入事务, 如果当前没有事务则新建一个(默认情况)
  2. Propagation.NOT_SUPPORTED(not_supported) : 以非事务方式执行操作,如果当前存在事务就把当前事务挂起,执行完后恢复事务(忽略当前事务);
  3. Propagation.SUPPORTS (supports) :如果当前有事务则加入,如果没有则不用事务。
  4. Propagation.MANDATORY (mandatory) :支持当前事务,如果当前没有事务,则抛出异常。(当前必须有事务)
  5. PROPAGATION_NEVER (never) :以非事务方式执行,如果当前存在事务,则抛出异常。(当前必须不能有事务)
  6. Propagation.REQUIRES_NEW (requires_new) :支持当前事务,如果当前有事务,则挂起当前事务,然后新创建一个事务,如果当前没有事务,则自己创建一个事务。
  7. Propagation.NESTED (nested 嵌套事务) :如果当前存在事务,则嵌套在当前事务中。如果当前没有事务,则新建一个事务自己执行(和required一样)。嵌套的事务使用保存点作为回滚点,当内部事务回滚时不会影响外部事物的提交;但是外部回滚会把内部事务一起回滚回去。(这个和新建一个事务的区别)

ps:本文就常用的 Rropagation.REQUIRED、Propagation.REQUIRES_NEW、Propagation.NESTED这三种进行案例演示。

二、Propagation.REQUIRED属性

            ①、实例代码:

@Service
public class TransactionService {
    @Autowired
    private UserService userService;
    @Autowired
    private EmpleeService empleeService;

    @Transactional
    public int transaction(User user, Emplyee emplyee) {
        int j = userService.addUser(user);
        int i = empleeService.addEmplyee(emplyee);
        System.out.println("userService-->" + j);
        System.out.println("empleeService-->" + i);
        return i;
    }
}

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private EmplyeeMapper emplyeeMapper;

    @Autowired
    private UserMapper userMapper;
    
    @Override
    @Transactional
    public int addUser(User user) {
        int i = userMapper.insert(user);
        return i;
    }
}


@Service
public class EmpleeServiceImpl implements EmpleeService {

    @Autowired
    private EmplyeeMapper emplyeeMapper;
    
    @Transactional
    @Override
    public int addEmplyee(Emplyee emplyee) {
        int i = emplyeeMapper.insert(emplyee);
        return i;
    }
}

 

            ②、Debug调试

当调用transactionService.transaction(user,emplyee)方法的时候,因为transactionService是代理对象,所以会调用到事务切面入口,如果外层方法上的事务传播属性Rropagation.REQUIRED、Propagation.REQUIRES_NEW、Propagation.NESTED是这三种,一开始逻辑代码是一摸一样的

下面直接到AbstractPlatformTransactionManager类getTransaction方法中的逻辑

接下来来到开启事务方法中的获取数据库连接并设置相关的属性,此时进入到 DataSourceTransactionManager类中的doBegin()

doBegin方法逻辑总结: 

  1. 创建事务对象,获取数据库连接对象并封装成connectHolder对象并设置到事务对象中。
  2. 设置事务的隔离级别
  3. 关闭自动提交事务
  4. ThreadLocal找那个建立数据源和连接的绑定关系

 下面直接到了具体方法的调用逻辑代码:

接着分析userService.addUser(user)调用的逻辑,此时也会进入到事务切面中,此时不会进入到doBegin方法中,会走到 类AbstractPlatformTransactionManager 的handleExistingTransaction方法,这个方法中所有的if判断都没进,只是把事务状态中newTransaction属性变为false

 下面走到了addUser方法的commit逻辑中,

private void processCommit(DefaultTransactionStatus status) throws TransactionException {
   try {
      boolean beforeCompletionInvoked = false;
      try {
         prepareForCommit(status);
         triggerBeforeCommit(status);
         triggerBeforeCompletion(status);
         beforeCompletionInvoked = true;
         boolean globalRollbackOnly = false;
         if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
            globalRollbackOnly = status.isGlobalRollbackOnly();
         }
//事务传播属性为NESTED,才会有回滚点
         if (status.hasSavepoint()) {
            if (status.isDebug()) {
               logger.debug("Releasing transaction savepoint");
            }
//擦除回滚点
            status.releaseHeldSavepoint();
         }
//只有是新事务才可以提交
         else if (status.isNewTransaction()) {
            if (status.isDebug()) {
               logger.debug("Initiating transaction commit");
            }
//提交事务
            doCommit(status);
         }
         // Throw UnexpectedRollbackException if we have a global rollback-only
         // marker but still didn't get a corresponding exception from commit.
         if (globalRollbackOnly) {
            throw new UnexpectedRollbackException(
                  "Transaction silently rolled back because it has been marked as rollback-only");
         }
      }
      catch (UnexpectedRollbackException ex) {
         // can only be caused by doCommit
         triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
         throw ex;
      }
      catch (TransactionException ex) {
         // can only be caused by doCommit
         if (isRollbackOnCommitFailure()) {
            doRollbackOnCommitException(status, ex);
         }
         else {
            triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
         }
         throw ex;
      }
      catch (RuntimeException ex) {
         if (!beforeCompletionInvoked) {
            triggerBeforeCompletion(status);
         }
         doRollbackOnCommitException(status, ex);
         throw ex;
      }
      catch (Error err) {
         if (!beforeCompletionInvoked) {
            triggerBeforeCompletion(status);
         }
         doRollbackOnCommitException(status, err);
         throw err;
      }

      // Trigger afterCommit callbacks, with an exception thrown there
      // propagated to callers but the transaction still considered as committed.
      try {
         triggerAfterCommit(status);
      }
      finally {
         triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
      }

   }
   finally {
      cleanupAfterCompletion(status);
   }
}

根据源码addUser不会自己提交事务,下面接着反射调用empleeService.addEmplyee方法,第三次进入事务切面

 此时的empleeService.addEmplyee调用跟addUser方法的流程是一样的。此处省略。。

最后的事务提交交给最外层中的Transaction方法来完成的。

            ③、正常情况伪代码流程

transaction外层方法的开始
    try{
        createTransactionIfNecessary 开启事务
        
        userService.addUser(user)
        addUser开始执行
        try{
            createTransactionIfNecessary 开启事务
            int i = userMapper.insert(user);
            return i;
            
        }catch (Throwable ex) {    
            //回滚事务
            completeTransactionAfterThrowing(txInfo, ex);
                    throw ex;
        }
        //提交事务
        commitTransactionAfterReturning(txInfo);
        addUser结束

        
         empleeService.addEmplyee(emplyee)
         addEmplyee开始执行
        try{
            createTransactionIfNecessary 开启事务
            int i = emplyeeMapper.insert(emplyee);
            return i;
        }catch (Throwable ex) {    
            //回滚事务
            completeTransactionAfterThrowing(txInfo, ex);
                    throw ex;
        }
        //提交事务
        commitTransactionAfterReturning(txInfo);
        addEmplyee结束

        
    }catch (Throwable ex) {    
        //回滚事务
        completeTransactionAfterThrowing(txInfo, ex);
                throw ex;
    }
    //提交事务
    commitTransactionAfterReturning(txInfo);
transaction外层方法的结束

            ④、代码添加异常

其他的不变。。。。
@Override
@Transactional
public int addUser(User user) {
    int i = userMapper.insert(user);
    if (true) {
        throw new RuntimeException("sss");
    }
    return i;
} 

           运行结果: 两张表中都没有添加数据,

           原因分析:addUser中发生异常,会调到自己的回滚代码,然而这时因为当前事务中的事务newTransaction是为false的,他自己不能进行事务的回滚操作,只能往外层继续抛,这时addEmplyee方法根本就没执行所以到了外层transaction方法的回滚。

            ⑤、经典案例一

代码示例:

@Transactional
public void transaction(User user, Emplyee emplyee) {
    try {
        TransactionSynchronizationManager.getResource(dataSourced);
        userService.addUser(user);
        System.out.println("userService-->");
        empleeService.addEmplyee(emplyee);
        System.out.println("empleeService-->");
    } catch (Exception e) {
        e.printStackTrace();
    }
}

addUser:

@Transactional
public int addUser(User user) {
    int i = userMapper.insert(user);
    return i;
}

addEmplyee:

@Transactional
public int addEmplyee(Emplyee emplyee) {
    int i = emplyeeMapper.insert(emplyee);
    if (true) {
        throw new RuntimeException("sss");
    }
    return i;
}

运行的伪代码:

transaction外层方法的开始
    try{

      try{
        createTransactionIfNecessary 开启事务
        
        userService.addUser(user)
        addUser开始执行
        try{
            createTransactionIfNecessary 开启事务
            int i = userMapper.insert(user);
            return i;
            
        }catch (Throwable ex) {    
            //回滚事务
            completeTransactionAfterThrowing(txInfo, ex);
                    throw ex;
        }
        //提交事务
        commitTransactionAfterReturning(txInfo);
        addUser结束

        
         empleeService.addEmplyee(emplyee)
         addEmplyee开始执行
        try{
            createTransactionIfNecessary 开启事务
            int i = emplyeeMapper.insert(emplyee);

          if (true) { throw new RuntimeException("sss"); }
            return i;
        }catch (Throwable ex) {    
            //回滚事务
            completeTransactionAfterThrowing(txInfo, ex);
                    throw ex;
        }
        //提交事务
        commitTransactionAfterReturning(txInfo);
        addEmplyee结束

          }catch (Throwable e) {    
               e.printStackTrace();
         }
    }catch (Throwable ex) {    
        //回滚事务
        completeTransactionAfterThrowing(txInfo, ex);
                throw ex;
    }
    //提交事务
    commitTransactionAfterReturning(txInfo);  //最终执行到这一步进入最外层提交
transaction外层方法的结束

结果:最终执行到Transaction方法的commitTransactionAfterReturning 逻辑中。但是在最后的结果中,两个表中的都没有插入数据。  why?源码跟读

            ​​​​​​⑦、分析:

在代码执行的过程中,addUser方法正常但执行没有资格提交事务,接着执行addEmplyee方法,发生了异常,会走到addEmplyee方法的回滚操作,debug来看他的回滚中做了哪些操作?

进入到TransactionAspectSupport类中completeTransactionAfterThrowing方法:

继续跟进回滚中做了哪些操作,来到了AbstractPlatformTransactionManager类中processRollback方法

private void processRollback(DefaultTransactionStatus status) {
   try {
      try {
         triggerBeforeCompletion(status);
         if (status.hasSavepoint()) {
            if (status.isDebug()) {
               logger.debug("Rolling back transaction to savepoint");
            }
            status.rollbackToHeldSavepoint();
         }
         else if (status.isNewTransaction()) {
//平时会走这个
            if (status.isDebug()) {
               logger.debug("Initiating transaction rollback");
            }
            doRollback(status);
         }
         else if (status.hasTransaction()) {

            if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
               if (status.isDebug()) {
                  logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
               }
             //会走到这里
               doSetRollbackOnly(status);
            }
            省略。。。
}

下面是doSetRollbackOnly方法具体逻辑:

接着来到了transaction方法commitTransactionAfterReturning方法进行提交

接着来到了AbstractPlatformTransactionManager类中commit方法

@Override
public final void commit(TransactionStatus status) throws TransactionException {
   if (status.isCompleted()) {
      throw new IllegalTransactionStateException(
            "Transaction is already completed - do not call commit or rollback more than once per transaction");
   }

   DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
   if (defStatus.isLocalRollbackOnly()) {
      if (defStatus.isDebug()) {
         logger.debug("Transactional code has requested rollback");
      }
      processRollback(defStatus);
      return;
   }
//有全局的异常,就会进入到这个if
   if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
      if (defStatus.isDebug()) {
         logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
      }
//回滚
      processRollback(defStatus);
      // Throw UnexpectedRollbackException only at outermost transaction boundary
      // or if explicitly asked to.
      if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
         throw new UnexpectedRollbackException(
               "Transaction rolled back because it has been marked as rollback-only");
      }
      return;
   }
//正常情况进入到这个commit
   processCommit(defStatus);
}

所以结果会全部进行回滚操作,这个是在Spring源码comiit操作中做了异常的回滚。

总结:

前提:所有的方法传播属性都是默认的时候

在Spring源码中,commit提交过程中不一定是做了真正的提交代码逻辑,当方法中有异常,并且在自己的代码中try cath捕获了,这时在自己方法中虽然不能回滚事务,但是它会最后的回滚逻辑中设置一个属性rollbackOnly为true,到了最往外层的提交事务逻辑中,会有对这个属性判断是否进行回滚操作。所以验证了,在Spring源码提交事务的过程中,不一定会真正的提交事务

            ⑧、经典案例二 

示例代码:

transaction方法:
   
@Transactional()
public void transaction(User user, Emplyee emplyee) throws Exception {
        System.out.println("userService-->");
        userService.addUser(user);
        TransactionSynchronizationManager.getResource(dataSourced);
        empleeService.addEmplyee(emplyee);
        System.out.println("empleeService-->");
}

addEmplyee:

@Transactional
@Override
public int addEmplyee(Emplyee emplyee) {
    int i = emplyeeMapper.insert(emplyee);
    return i;
}

addUser代码:

@Override
@Transactional()
public int addUser(User user) throws Exception {
    int i = userMapper.insert(user);
    if (true) throw new Exception("aa");
    return i;
}

 结论:addUser表中插入成功有一条数据,addEmplyee没有执行,所以没数据。

            ⑨、伪代码案列二 

transaction外层方法的开始
    try{


        createTransactionIfNecessary 开启事务
        
        userService.addUser(user)
        addUser开始执行
        try{
            createTransactionIfNecessary 开启事务
            int i = userMapper.insert(user);

if (true) { throw new Exception("sss"); }
            return i;
            
        }catch (Throwable ex) {    
            //回滚事务
            completeTransactionAfterThrowing(txInfo, ex);
                    throw ex;
        }
        //提交事务
        commitTransactionAfterReturning(txInfo);
        addUser结束

        
         empleeService.addEmplyee(emplyee)
         addEmplyee开始执行
        try{
            createTransactionIfNecessary 开启事务
            int i = emplyeeMapper.insert(emplyee);

            return i;
        }catch (Throwable ex) {    
            //回滚事务
            completeTransactionAfterThrowing(txInfo, ex);
                    throw ex;
        }
        //提交事务
        commitTransactionAfterReturning(txInfo);
        addEmplyee结束

       
    }catch (Throwable ex) {    
        //回滚事务
        completeTransactionAfterThrowing(txInfo, ex);
                throw ex;
    }
    //提交事务
    commitTransactionAfterReturning(txInfo);  //最终执行到这一步进入最外层提交
transaction外层方法的结束

            ⑩、分析二 

首先第一次进入切面中逻辑是一样的,到了addUser方法执行逻辑,因为addUser中抛出了异常所以直接进入到回滚代码逻辑中,

protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
   if (txInfo != null && txInfo.hasTransaction()) {
      if (logger.isTraceEnabled()) {
         logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
               "] after exception: " + ex);
      }
//如果抛出的异常事属性中定义的异常或者他的子类则回滚,否则判断父类中的方法,父类中判断的异常类型是运行时异常或者erro则返回true
      if (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 ex2) {
            logger.error("Application exception overridden by rollback exception", ex);
            throw ex2;
         }
         catch (Error err) {
            logger.error("Application exception overridden by rollback error", ex);
            throw err;
         }
      }
      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 ex2) {
            logger.error("Application exception overridden by commit exception", ex);
            throw ex2;
         }
         catch (Error err) {
            logger.error("Application exception overridden by commit error", ex);
            throw err;
         }
      }
   }
}

 

 从回滚的逻辑代码中可以看出,回滚是要判断异常的类型的,因为addUser中抛出异常不匹配,所以会进入到commit提交的逻辑,但是此时addUser中的事务状态newTransaction为false,所以提交事物的操作还得交给外层的Transaction方法中。因此addUser会插如数据成功。

总结:

 在Spring事务回滚中,不一定就是真正的回滚事务,也可能会做事务的提交。在回滚的逻辑中会做方法异常类型判断,只有是异常类型匹配才会进行相应的回滚操作。

 

二、Propagation.REQUIRED_NEW属性

             ①、实例代码:

    @Transactional
    public void transaction(User user, Emplyee emplyee) {
        try {
            TransactionSynchronizationManager.getResource(dataSourced);
            userService.addUser(user);
            System.out.println("userService-->");
            empleeService.addEmplyee(emplyee);
            System.out.println("empleeService-->");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public int addUser(User user) throws Exception {
        int i = userMapper.insert(user);
        return i;
    }

    @Transactional
    @Override
    public int addEmplyee(Emplyee emplyee) {
        int i = emplyeeMapper.insert(emplyee);
        return i;
    }

             ②、源码跟读:

前面的操作基本都是相同的,只不过是addUser方法中的隔离级别变成了Propagation.REQUIRES_NEW,接下来直接到addUser方法的调用切面中

在类中 AbstractPlatformTransactionManager中handleExistingTransaction方法

//省略。。。。
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
   if (debugEnabled) {
      logger.debug("Suspending current transaction, creating new transaction with name [" +
            definition.getName() + "]");
   }
   SuspendedResourcesHolder suspendedResources = suspend(transaction);
   try {
      boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
      DefaultTransactionStatus status = newTransactionStatus(
            definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
//从这创建的是addUser的绑定关系
      doBegin(transaction, definition);
      prepareSynchronization(status, definition);
      return status;
   }
   catch (RuntimeException beginEx) {
      resumeAfterBeginException(transaction, suspendedResources, beginEx);
      throw beginEx;
   }
   catch (Error beginErr) {
      resumeAfterBeginException(transaction, suspendedResources, beginErr);
      throw beginErr;
   }
}

省略。。。

唯一不同的是在doBegin方法前面中做了挂起的操作,下面分析挂起逻辑:类 DataSourceTransactionManager   方法doSuspend

 

protected final SuspendedResourcesHolder suspend(Object transaction) throws TransactionException {
   if (TransactionSynchronizationManager.isSynchronizationActive()) {
      List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
      try {
         Object suspendedResources = null;
         if (transaction != null) {
//拿到挂起的连接对象
            suspendedResources = doSuspend(transaction);
         }
         String name = TransactionSynchronizationManager.getCurrentTransactionName();
         TransactionSynchronizationManager.setCurrentTransactionName(null);
         boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
         TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
         Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
         TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
         boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
         TransactionSynchronizationManager.setActualTransactionActive(false);
         return new SuspendedResourcesHolder(
               suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
      }
      省略。。。。
}

核心代码:

@Override
protected Object doSuspend(Object transaction) {
//上方法的连接对象置null
   DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
   txObject.setConnectionHolder(null);
//解除数据源和连接对象的绑定关系
   return TransactionSynchronizationManager.unbindResource(this.dataSource);
}

然后就是addUser自己提交自己的事务了,他是一个单独的事务连接,新建的,newTransaction为true,addUser在提交完事务以后需要做那些事情呢?

在提交代码中的逻辑

private void processCommit(DefaultTransactionStatus status) throws TransactionException {
   try {
      boolean beforeCompletionInvoked = false;
      try {
        //省略。。。。。
         if (status.hasSavepoint()) {
            if (status.isDebug()) {
               logger.debug("Releasing transaction savepoint");
            }
            status.releaseHeldSavepoint();
         }
         else if (status.isNewTransaction()) {
//提交事务
            if (status.isDebug()) {
               logger.debug("Initiating transaction commit");
            }
            doCommit(status);
         }
         省略。。。。。。
   finally {
      cleanupAfterCompletion(status);
   }
}

 来看一下这个cleanupAfterCompletion方法做了哪些事情,类 AbstractPlatformTransactionManager  

下面是释放资源中的 releaseConnection

下面看这个resume方法:

protected final void resume(Object transaction, SuspendedResourcesHolder resourcesHolder)
      throws TransactionException {
//如果存在挂起资源
   if (resourcesHolder != null) {
      Object suspendedResources = resourcesHolder.suspendedResources;
      if (suspendedResources != null) {
//将挂起的资源,恢复绑定关系
         doResume(transaction, suspendedResources);
      }
      List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
      if (suspendedSynchronizations != null) {
         TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
         TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
         TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
         TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
         doResumeSynchronization(suspendedSynchronizations);
      }
   }
}

到此后面的逻辑就一样了,后面就不一一说明了。

二、Propagation.REQUIRED_NSTED属性

             ①、示例代码:

@Transactional()
public void transaction(User user, Emplyee emplyee) throws Exception {
    System.out.println("empleeService-->");
    userService.addUser(user);
    TransactionSynchronizationManager.getResource(dataSourced);
    empleeService.addEmplyee(emplyee);
    System.out.println("userService-->");
}
@Transactional(propagation = Propagation.NESTED)
public int addUser(User user) {
    int i = userMapper.insert(user);
    return i;
}
@Transactional(propagation = Propagation.NESTED)
public int addEmplyee(Emplyee emplyee) {
    int i = emplyeeMapper.insert(emplyee);
    if (true) throw new RuntimeException("sss");
    return i;
}

             ②、核心源码

在创建事务的方法中会有属性为REQUIRED_NSTED的处理逻辑,再类中 AbstractPlatformTransactionManager  方法:handleExistingTransaction

省略。。。
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 (debugEnabled) {
      logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
   }
//useSavepointForNestedTransaction 默认为true
   if (useSavepointForNestedTransaction()) {
      // Create savepoint within existing Spring-managed transaction,
      // through the SavepointManager API implemented by TransactionStatus.
      // Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
//事务状态为false
      DefaultTransactionStatus status =
            prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
//创建回滚点,里面就是java手动创建回滚点的APi      
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.
      boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
      DefaultTransactionStatus status = newTransactionStatus(
            definition, transaction, true, newSynchronization, debugEnabled, null);
      doBegin(transaction, definition);
      prepareSynchronization(status, definition);
      return status;
   }
}

省略。。。

 其他的操作是一样的,在addUser中来到事务提交的逻辑中,此时addUser中是有回滚点的,看提交事务中做了哪些操作。

接着到了addEmplyee方法执操作,这时也会创建回滚点,到被代理方法调用方法之后有异常则进入回滚代码中,此时会进入到红色代码段中

private void processRollback(DefaultTransactionStatus status) {
   try {
      try {
         triggerBeforeCompletion(status);
//是否有回滚点
         if (status.hasSavepoint()) {
            if (status.isDebug()) {
               logger.debug("Rolling back transaction to savepoint");
            }
        //回滚到回滚点,并将回滚点设置为空
            status.rollbackToHeldSavepoint();
         }
         else if (status.isNewTransaction()) {
            if (status.isDebug()) {
               logger.debug("Initiating transaction rollback");
            }
            doRollback(status);
         }
         else if (status.hasTransaction()) {
            if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
               if (status.isDebug()) {
                  logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
               }
               doSetRollbackOnly(status);
            }
            else {
               if (status.isDebug()) {
                  logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
               }
            }
         }
         省略。。。。。。
}

 结果:两个表中都没数据,why?   那么addEmplyee方法中出现异常回滚了在我们意料之中,此时这个addUser也回滚了,这不就违背了Nested按照回滚点回滚的作用了么?改造代码

@Transactional()
public void transaction(User user, Emplyee emplyee) throws Exception {
try{
    System.out.println("empleeService-->");
    userService.addUser(user);
    TransactionSynchronizationManager.getResource(dataSourced);
    empleeService.addEmplyee(emplyee);
    System.out.println("userService-->");
 } catch (Exception e){
     e.printStackTrace();
   }
}
其他的代码不变

上述改造后的代码运行结果,addUser执行成功,addEmplyee照常失败。实现了按照回滚点回滚了。

分析:

NESTED和EQUIRED区别
  1. 首先上述的中(经典案例一),当代码中有异常并且在外层调用方法中添加了try catch捕获了会修改rollbackOnly属性为true,导致在最后外层中commit提交事务做了回滚操作。
  2. 而在NESTED传播属性中,代码一样的前提下,实现了按照回滚点回滚的操作。其他的基本和REQUIRE一样。

二、总结:

  1. 事务跟连接对象挂钩的
  2. 事务的传播属性就是方法中事务的流转的
  3. 只要被Spring内部try catch捕获到,并且这个异常RuntimeException或者Error才会执行回滚操作(很重要!!!)
  4. 事务中的提交逻辑中不一定会真正的提交事务也有可能回滚事务,在回滚事务中也会判断异常的类型是否匹配,如果匹配才会进行回滚操作。
  5. 如果最外层方法上的事务传播属性Rropagation.REQUIRED、Propagation.REQUIRES_NEW、Propagation.NESTED是这三种,一开始逻辑代码是一摸一样的。这一点很重要!!
  6. 还有就是在原理不台熟悉的一定要写伪代码来看事务是怎么传播的

三、Spring相关笔记链接

  1. 叶良辰の学习笔记:https://yangzhiwen911.github.io/

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值