1. 程式人生 > >Mybatis 原始碼分析:事物管理

Mybatis 原始碼分析:事物管理

1. mybatis 事物概述

mybatis 使用 Transaction 介面封裝了資料庫連線 Connection 的生命週期,它由 creation、preparation、commit/rollback 和 close 組成。

public interface Transaction {
	Connection getConnection() throws SQLException;

  	void commit() throws SQLException;

  	void rollback() throws SQLException;

  	void close() throws
SQLException; }

mybatis 使用兩種機制來管理事物:

  • 使用 JDBC 的事務管理機制:

    即利用 java 提供的 Connection 物件完成對事務的 commit、rollback 和 close。

  • 使用 MANAGED 的事務管理機制

    這種機制 myBatis 自身不會去實現事務管理,而是讓程式的容器(如 JBOSS,Spring)來實現對事務的管理。

Transaction

2. mybatis 事物原理

mybatis 會根據如下配置來決定使用何種方式管理資料庫事物。

<configuration>
	<environment>
		<transactionManager
type="JDBC" />
</environment> </configuration>

因此,mybatis 在解析配置檔案 <environment /> 元素時,會生成具體的 TransactionFactory 物件。

private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
        environment = context.getStringAttribute
("default"); } for (XNode child : context.getChildren()) { String id = child.getStringAttribute("id"); if (isSpecifiedEnvironment(id)) { TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); DataSource dataSource = dsFactory.getDataSource(); Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); configuration.setEnvironment(environmentBuilder.build()); } } } }
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      Properties props = context.getChildrenAsProperties();
      // 獲取具體的 事物工廠
      TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a TransactionFactory.");
  }

在上面提到,mybatis 提供兩種機制來管理事物:JdbcTransaction 和 ManagedTransaction。這可以看成是兩種型別產品,因此 mybatis 使用了抽象工廠模式來根據不同的配置來建立具體的事物管理機制。 TransactionFactory 但是現在還只是得到能夠生產 Transaction 的工廠,那麼 mybatis 是從哪開始通過 TransactionFactory 來具體構建事物管理物件呢?由於 mybatis 執行資料庫操作都是通過 SqlSession 來的,那麼可以推測出應該是在得到 SqlSession 物件時,把這些東西都準備好。那就看看 DefaultSqlSession 的 openSession() 方法是不是跟推測的一樣。

@Override
public SqlSession openSession() {
	// 預設 executor 是 SimpleExecutor,且預設 autoCommit 為 false
	return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      // 從 Environment 物件中獲取之前建立好的 TransactionFactory
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      // 關鍵:生成 Transaction 物件
      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();
    }
  }

我們已 JdbcTransactionFactory 為例,因為 ManagedTransactionFactory 生成的 Transaction 並不會做 commit 和 rollback 操作,它會交由容器去管理,如 Spring。

@Override
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
	return new JdbcTransaction(ds, level, autoCommit);
}

目前,我們已經知道 mybatis 是通過什麼來管理事物,那麼在我們具體呼叫 SqlSession 物件的 update 或 delete 操作它又是如何工作的呢?

@Override
  public int update(String statement, Object parameter) {
    try {
      dirty = true;
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

可以看到,update 操作會設定 dirty 為 true,那這個變數又有什麼用?我們可以看看 commit() 和 rollback() 方法。 PS:delete 操作其實底層呼叫的是 update 方法

@Override
public void commit() {
	commit(false);
}
@Override
public void commit(boolean force) {
	try {
	  executor.commit(isCommitOrRollbackRequired(force));
	  dirty = false;
	} catch (Exception e) {
	  throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + e, e);
	} finally {
	  ErrorContext.instance().reset();
	}
}
private boolean isCommitOrRollbackRequired(boolean force) {
	return (!autoCommit && dirty) || force;
}

在這個方法中,我們可以看到 mybatis 是通過使用三個值來決定是否需要 commit 或 rollback。預設情況下 force 為 false,而 dirty 肯定為 true,因此只要 autoCommit 為 false,就需要 commit 或 rollback,否則不需要。 rollback 的原理跟 commit 是一樣的,就不再贅述了~~~