Mybatis源码分析——事务

我们平时使用db都离不开事务,那在mybatis中是如何实现事务的呢。

mybatis中有事务Transaction,必有生产事务的TransactionFactory。

TransactionFactory:有3个实现类,分别是JdbcTransactionFactory、ManagedTransactionFactory和SpringManagedTransactionFactory,前2者是mybatsi自带的,第3者则是mybatis-spring包中的。前2者都比较简单,本章重点介绍SpringManagedTransactionFactory。

Transaction:也同样有3个实现类,跟上面的Factory相对应,分别是JdbcTransaction、ManagedTransaction和SpringManagedTransaction。

 

我们接着看,在spring集成mybatis中是如何使用事务的。在解析mybatis-config.xml配置文件的时候,如果没有指定事务工厂,就默认使用SpringManagedTransactionFactory,如下

    if (this.transactionFactory == null) {
      this.transactionFactory = new SpringManagedTransactionFactory();
    }

    configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));

然后在DefaultSqlSessionFactory.openSession时获取事务工厂,生产事务并绑定到Executor,如下

  @Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
  private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
    if (environment == null || environment.getTransactionFactory() == null) {
      return new ManagedTransactionFactory();
    }
    return environment.getTransactionFactory();
  }
  @Override
  public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
    return new SpringManagedTransaction(dataSource);
  }

可以看到,调用事务工厂的newTransaction时,返回了SpringManagedTransaction实例。

 

那又是何时通过事务获取数据库连接的呢,答案是在Executor获取Statement的时候,SimpleExecutor的prepareStatement,如下

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    handler.parameterize(stmt);
    return stmt;
  }

这里调用的getConnection,会调用它的父类BaseExecutor的getConnection,如下

  protected Connection getConnection(Log statementLog) throws SQLException {
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
      return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
      return connection;
    }
  }

然后在这里就调用Transaction获取Connection。

 

好了,我们再来看看Transaction都有哪些方法吧!SpringManagedTransaction继承于Transaction,有5个方法,如下

/**
   * Retrieve inner database connection
   * @return DataBase connection
   * @throws SQLException
   */
  Connection getConnection() throws SQLException;

  /**
   * Commit inner database connection.
   * @throws SQLException
   */
  void commit() throws SQLException;

  /**
   * Rollback inner database connection.
   * @throws SQLException
   */
  void rollback() throws SQLException;

  /**
   * Close inner database connection.
   * @throws SQLException
   */
  void close() throws SQLException;

  /**
   * Get transaction timeout if set
   * @throws SQLException
   */
  Integer getTimeout() throws SQLException;

我们一个一个来看吧,先看getConnection,如下

  @Override
  public Connection getConnection() throws SQLException {
    if (this.connection == null) {
      openConnection();
    }
    return this.connection;
  }
  private void openConnection() throws SQLException {
    this.connection = DataSourceUtils.getConnection(this.dataSource);
    this.autoCommit = this.connection.getAutoCommit();
  this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);
  }

这里是通过spring的一个工具类来获取connection,如下

public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
		try {
			return doGetConnection(dataSource);
		}
		catch (SQLException ex) {
			throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
		}
	}

真正干活的是下面do打头的doGetConnection,如下

public static Connection doGetConnection(DataSource dataSource) throws SQLException {
		Assert.notNull(dataSource, "No DataSource specified");

		ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
		if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
			conHolder.requested();
			if (!conHolder.hasConnection()) {
				logger.debug("Fetching resumed JDBC Connection from DataSource");
				conHolder.setConnection(dataSource.getConnection());
			}
			return conHolder.getConnection();
		}
		// Else we either got no holder or an empty thread-bound holder here.

		logger.debug("Fetching JDBC Connection from DataSource");
		Connection con = dataSource.getConnection();

		if (TransactionSynchronizationManager.isSynchronizationActive()) {
			logger.debug("Registering transaction synchronization for JDBC Connection");
			// Use same Connection for further JDBC actions within the transaction.
			// Thread-bound object will get removed by synchronization at transaction completion.
			ConnectionHolder holderToUse = conHolder;
			if (holderToUse == null) {
				holderToUse = new ConnectionHolder(con);
			}
			else {
				holderToUse.setConnection(con);
			}
			holderToUse.requested();
			TransactionSynchronizationManager.registerSynchronization(
					new ConnectionSynchronization(holderToUse, dataSource));
			holderToUse.setSynchronizedWithTransaction(true);
			if (holderToUse != conHolder) {
				TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
			}
		}

		return con;
	}

这里从事务管理器中获取ConnectionHolder,然后从Holder中获取Connection,如果没有,就直接从DataSource里获取,当然这里的DataSource其实就是我们的线程池的实现类了,关于线程池,留到后面讲,这里不展开。

如果获取到的ConnectionHolder是空的,就会新建一个Holder,并把它注册到事务管理器中。这里的TransactionSynchronizationManager是做了同步锁处理的。

回到openConnection()方法,isConnectionTransactional是标记连接是否在事务管理器中。

接着看commit()方法,如下

  @Override
  public void commit() throws SQLException {
    if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) {
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Committing JDBC Connection [" + this.connection + "]");
      }
      this.connection.commit();
    }
  }

这里会判断,如果不受事务管理器管理,并且不是自动提交,就会执行connection的commit提交。如果是在事务管理器中的话,就是在事务管理器的commit里做处理。

再来看rollback()方法,如下

  @Override
  public void rollback() throws SQLException {
    if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) {
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Rolling back JDBC Connection [" + this.connection + "]");
      }
      this.connection.rollback();
    }
  }

跟commit是一样的处理逻辑。

最后看一下close()方法,如下

  @Override
  public void close() throws SQLException {
    DataSourceUtils.releaseConnection(this.connection, this.dataSource);
  }

这里的关闭,其实也并不是真正的关闭,最终还是交给事务管理器控制,归还连接池而已。

 

好了,介绍完了SpringManagedTransaction,我们再来看看上面多次提到的事务管理器吧!这个其实不在mybatis的范畴了,而是属于spring的内容,就简单的说一下吧。

我们在spring-transaction.xml文件中会配置事务管理器,如下


	
		
	
	
	
	
		
			
			
			
			
		
	
	
	

	
		
		
	

这里就会指定事务管理器为DataSourceTransactionManager,它继承于AbstractPlatformTransactionManager implements PlatformTransactionManager,主要的方法有:如下

TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

void commit(TransactionStatus status) throws TransactionException;

void rollback(TransactionStatus status) throws TransactionException;

就是获取事务,提交和回滚。关于事务管理器,后面分析spring源码的时候再做介绍。


上一篇  Mybatis源码分析——sql执行过程的类图

你可能感兴趣的:(Mybatis)