Spring Transaction 事务提交

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/V_Junk/article/details/81712678

transaction 第二部分

之所以称之为第二部分是因为这部分主要在 JdkDynamicAopProxy 类,之后的操作。第一次读源码,有啥偏差的地方,大虾们拍砖拍砖……

时序图

  • 开始之前先看一个时序图(画的不好,估计也就我自己看得懂 emmmmmmm)
    这里写图片描述
    这个图,一个简单的流程。如果非要说 UserService.save() 方法为啥直接到 JdkDynamicAopProxy.invoke() 方法。那是动态代理的结果,也叫动态增强,关于那部分的内容下篇博客会概括,预计2天后的周五吧。还有,这样类似的博客有很多

环境配置 (部分)

  • Spring xml配置文件
    <tx:annotation-driven/>

    <bean id="userService" class="com.yhj.jdbc.service.impl.UserServiceI">
        <property name="dataSource" ref="dataSource"/>
        <property name="gameService" ref="gameService"/>
    </bean>

    <bean id="gameService" class="com.yhj.jdbc.service.impl.GameServiceI"/>

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
  • Java 实现类
@Transactional(propagation = Propagation.REQUIRES_NEW)
public class GameServiceI implements GameService {
    @Override
    public int queryALlGames() {
        System.out.println("queryALlGames");
        throw new RuntimeException("手动抛出的异常");
    }
}

@Transactional(propagation = Propagation.REQUIRED)
public class UserServiceI implements UserService {

    private JdbcTemplate jdbcTemplate;

    private GameService gameService;

    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public void setGameService(GameService gameService) {
        this.gameService = gameService;
    }

    @Override
    public void save(User user) {
        try {
            gameService.queryALlGames();
        } catch (Exception e) {
            e.printStackTrace();
        }

        jdbcTemplate.update("INSERT INTO user(studentname,studentnumber) VALUES (?,?)", new Object[]{user.getEmployeeid(), user.getUsername()}, new int[]{Types.VARCHAR, Types.VARCHAR});
    }
}

干正事

再声明一下,因为动态代理的时候,会涉及到一些类的注册,从静态代理开始可能会比较容易不知道,注册的类是用来干嘛的以及在什么时候用。
代理时:在 JdkDynamicAopProxy.invoke() 方法的如下代码:
1. 获取 TransactionInterceptor

    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

通过从 BeanFactoryTransactionAttributeSourceAdvisor 获取 TransactionInterceptor(在解析自定义标签时注入)。

  1. 通过 ReflectiveMethodInvocation 调用 TransactionInterceptor.invoke() 方法
    invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
    retVal = invocation.proceed();

跟踪代码到 org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction() 方法。参见部分代码:
这里写图片描述

  1. 获取事务属性
    如:PROPAGATION_REQUIRED,ISOLATION_DEFAULT
  2. 获取事务管理器 transactionManager
    org.springframework.transaction.support.AbstractPlatformTransactionManager
  3. 获取连接点
    如:com.yhj.jdbc.service.impl.UserServiceI.save()
  4. 创建一个事务,方法 createTransactionIfNecessary()
  5. 调用 service 中被代理的方法
  6. 异常处理
  7. 清理资源
  8. 提交事务

乍一看,就是这样,已经没有了……

获取事务 createTransactionIfNecessary

在这个方法内部通过 AbstractPlatformTransactionManager.getTransaction() 处理了各种事务逻辑。如下图:
createTransactionIfNecessary

可以看出来,如果当前已经存在(当前线程 ConnectionHolder 熟悉不为空且 isTransactionActive()true )事务就直接通过 handleExistingTransaction 方法返回了。从代码中也明显能看出来,只有
PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED 才对事务有支持。doBegin() 方法:主要设置当前线程连接的 ConnectionHloder,timeout,isolation 如果时新连接还需要绑定资源,在这个方法里面,关闭了 jdbc 的自动提交,由 Spring 控制事务。

    if (con.getAutoCommit()) {
                txObject.setMustRestoreAutoCommit(true);
                if (logger.isDebugEnabled()) {
                    logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
                }
                con.setAutoCommit(false);
    }

当前线程已经存在事务 handleExistingTransaction

Spring 中时允许事务嵌套的,关于嵌套事务的处理就在 handleExistingTransaction() 方法中。Spring 中代码如下:
这里写图片描述
这里面都会把当前的事务挂起,所谓的挂起就清空当前的事务状态,然后创建一个新的 SuspendedResourcesHolder 对象。

事务挂起 suspend

关键代码如下:
事务挂起

这里面都会把当前的事务挂起,所谓的挂起就清空当前的事务状态,然后创建一个新的 SuspendedResourcesHolder 对象

异常处理 completeTransactionAfterThrowing

    if (txInfo.transactionAttribute.rollbackOn(ex)) {
                try {
                    txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
                }
    }

// org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn
 public boolean rollbackOn(Throwable ex) {
        return (ex instanceof RuntimeException || ex instanceof Error);
 }

可以看出,默认只对 RuntimeExceptionError 回滚。

处理回滚的代码: org.springframework.transaction.support.AbstractPlatformTransactionManager#processRollback
这里写图片描述

如果是一个新的事务,就直接执行回滚。若不是,则发生异常的时候就会把当前事务的状态设置为 readOnly。所以一个事务只能被提交一次,如果是嵌套事务,就会通过 cleanupAfterCompletion() 方法恢复被挂起的事务,详情如下:

private void cleanupAfterCompletion(DefaultTransactionStatus status) {
        status.setCompleted();
        if (status.isNewSynchronization()) {
            TransactionSynchronizationManager.clear();
        }
        if (status.isNewTransaction()) {
            doCleanupAfterCompletion(status.getTransaction());
        }
        if (status.getSuspendedResources() != null) {
            if (status.isDebug()) {
                logger.debug("Resuming suspended transaction after completion of inner transaction");
            }
            resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());
        }
    }
  • 如题:如果有被挂起的事务,就将其恢复。关于事务提交和回滚的,我就目前只关注了上面这些内容,其实里面有一些触发器,还是比较重要。

猜你喜欢

转载自blog.csdn.net/V_Junk/article/details/81712678