spring 事务源码(三)如何保证被@Transactional标记方法中的所有sql都在一个事务内

7 篇文章 0 订阅
6 篇文章 0 订阅

系列文章

spring 事务源码(三)如何保证被@Transactional标记方法中的所有sql都在一个事务内

spring 事务源码(二)事务切面详细

spring 事务源码(一)事务切面注入、解析

核心问题

spring 如何保证被事务注解标记方法中的所有sql都在一个事务内

测试代码

还是之前的代码,但是这次得改下,想办法让sql抛出异常

<select id="getNum" resultType="java.lang.Integer">
        select count(*)
        from ss
        where 1 = 1;
    </select>
 @Transactional
    public void test(){
        courseItemService.ss();

    }

public void ss(){
        baseMapper.getNum();
    }

运行项目可以看到如下异常堆栈

在这里插入图片描述

最开始调用myabtis的类开始网上查就能查到

源码

org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor#invoke

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  // 获取sqlsession
  SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
      SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
  try {
    Object result = method.invoke(sqlSession, args);
    if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
      // 如果当前线程上下文没有事务,则自动提交
      sqlSession.commit(true);
    }
    return result;
  } catch (Throwable t) {
    Throwable unwrapped = unwrapThrowable(t);
    if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
      // 关闭链接
      closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
      sqlSession = null;
      .....
    }
    throw unwrapped;
  } finally {
    if (sqlSession != null) {
      closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
    }
  }
}

org.mybatis.spring.SqlSessionUtils#getSqlSession(.....)

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {

		// 获取当前线程中的sqlsession持有对象
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
		// 获取sqlsession
    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
      return session;
    }
		// 如果sqlsession不存在则创建
    session = sessionFactory.openSession(executorType);

   // 注册sqlsession 到当前线程上下文
    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

    return session;
  }

org.mybatis.spring.SqlSessionUtils#registerSessionHolder

private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,
    PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
  SqlSessionHolder holder;
  // 如果当前线程上下文有正在运行的事务
  if (TransactionSynchronizationManager.isSynchronizationActive()) {
    Environment environment = sessionFactory.getConfiguration().getEnvironment();

    if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
      LOGGER.debug(() -> "Registering transaction synchronization for SqlSession [" + session + "]");

      holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
      // 将SqlSessionHolder 绑定到当前线程
      TransactionSynchronizationManager.bindResource(sessionFactory, holder);
      TransactionSynchronizationManager
          .registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
      holder.setSynchronizedWithTransaction(true);
      holder.requested();
    } else {
      .........
    }
  } else {
    .....
  }

}

总结

原理其实很简单,就是在当前线程上下文中放置一个sqlsession对象,方法中任何一个sql执行先去判断是否存在,如果存在则用存在的,否则去创建一个新的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值