感觉还不是很明白,写本篇做一个开头记录把。
本想从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.实现在线程下获取同一个数据库连接。