spring 的transaction注解是如何作用到mybatis的

感觉还不是很明白,写本篇做一个开头记录把。

本想从spring的角度全局出发去了解它的整个数据库->jdbc->orm,理想的结构是这样。但是从mybatis来看,看起来并没有那么干净,比如DataSource的传递感觉就不是那么漂亮,在我们引用DruidDataSource或者是其他什么DruidDataSource的单个DruidDataSource的情况下,连接是怎么传递的,事务是怎么处理的等等。单从spring到ibatis(mybatis以前的叫法)来稍微的了解下。可惜英语能力还是不够,注释阅读起来还是太费劲。

由于没有弄清楚spring的datasource下面那几个关键类,所以从mybatis反推到spring单独考虑单数据源这一个分支,从mybatis的SqlSessionFactoryBean代码开始来看,先看dataSource的初始化。

/**
 * Set the JDBC {@code DataSource} that this instance should manage transactions for. The {@code DataSource} should
 * match the one used by the {@code SqlSessionFactory}: for example, you could specify the same JNDI DataSource for
 * both.
 *
 * A transactional JDBC {@code Connection} for this {@code DataSource} will be provided to application code accessing
 * this {@code DataSource} directly via {@code DataSourceUtils} or {@code DataSourceTransactionManager}.
 *
 * The {@code DataSource} specified here should be the target {@code DataSource} to manage transactions for, not a
 * {@code TransactionAwareDataSourceProxy}. Only data access code may work with
 * {@code TransactionAwareDataSourceProxy}, while the transaction manager needs to work on the underlying target
 * {@code DataSource}. If there's nevertheless a {@code TransactionAwareDataSourceProxy} passed in, it will be
 * unwrapped to extract its target {@code DataSource}.
 *
 * @param dataSource
 *          a JDBC {@code DataSource}
 *
 */
public void setDataSource(DataSource dataSource) {
  if (dataSource instanceof TransactionAwareDataSourceProxy) {
    // If we got a TransactionAwareDataSourceProxy, we need to perform
    // transactions for its underlying target DataSource, else data
    // access code won't see properly exposed transactions (i.e.
    // transactions for the target DataSource).
    this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
  } else {
    this.dataSource = dataSource;
  }
}

注释真的给看看麻了,就看懂一个意思应用代码以DataSource作为参数通过DataSourceUtils或者DataSourceTransactionManager获取一个支持事务的数据库连接(以下简称事务连接)。继续查找事务相关代码,在继续往下看在SqlSessionFactoryBean代码找到下面代码

targetConfiguration.setEnvironment(new Environment(this.environment,
    this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
    this.dataSource));

继续看SpringManagedTransactionFactory,返回的是一个SpringManagedTransaction

public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
  return new SpringManagedTransaction(dataSource);
}

进入SpringManagedTransaction重点看获取数据库连接的代码,可以看到引用了上面提到的DataSourceUtils。属于spring jdbc包的工具类。到这我们回到spring的DataSourceUtils和DataSourceTransactionManager。

private void openConnection() throws SQLException {
  this.connection = DataSourceUtils.getConnection(this.dataSource);
  this.autoCommit = this.connection.getAutoCommit();
  this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);

  LOGGER.debug(() -> "JDBC Connection [" + this.connection + "] will"
      + (this.isConnectionTransactional ? " " : " not ") + "be managed by Spring");
}

从DataSourceUtils的doGetConnection方法和DataSourceTransactionManager的doBegin方法来看他们都是通过TransactionSynchronizationManager来处理事务连接的。当他判断开启事务的时候会

// Bind the connection holder to the thread.
if (txObject.isNewConnectionHolder()) {
   TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}

进入我们会看到一系列的threadlocal。这就是处理数据库连接和线程关系的地方

private static final ThreadLocal<Map<Object, Object>> resources =
      new NamedThreadLocal<>("Transactional resources");

private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
      new NamedThreadLocal<>("Transaction synchronizations");

private static final ThreadLocal<String> currentTransactionName =
      new NamedThreadLocal<>("Current transaction name");

private static final ThreadLocal<Boolean> currentTransactionReadOnly =
      new NamedThreadLocal<>("Current transaction read-only status");

private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
      new NamedThreadLocal<>("Current transaction isolation level");

private static final ThreadLocal<Boolean> actualTransactionActive =
      new NamedThreadLocal<>("Actual transaction active");

这里只看bindResource

public static void bindResource(Object key, Object value) throws IllegalStateException {
   Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
   Assert.notNull(value, "Value must not be null");
   Map<Object, Object> map = resources.get();
   // set ThreadLocal Map if none found
   if (map == null) {
      map = new HashMap<>();
      resources.set(map);
   }
   Object oldValue = map.put(actualKey, value);
   // Transparently suppress a ResourceHolder that was marked as void...
   if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
      oldValue = null;
   }
   if (oldValue != null) {
      throw new IllegalStateException("Already value [" + oldValue + "] for key [" +
            actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
   }
   if (logger.isTraceEnabled()) {
      logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" +
            Thread.currentThread().getName() + "]");
   }
}

使用dataSource作为key,connectionHolder作为vallue,从这里我们可以看出是可以支持多数据源的。提供的主要方法就是绑定和解绑。有一个重点是全部都是static方法,这意味着,从任何地方都可以直接调用获取当前线程对应的数据库事务连接。

我们知道spring transaction注解的实现是aop环绕增强。

总结

mybatis通过引用spring的DataSourceUtils来实现spring事务。DataSourceUtils通过TransactionSynchronizationManager的threadlocal来将datasource作为key,connection作为连接存储到Threadlocal.实现在线程下获取同一个数据库连接。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值