Transaction-Mybatis源碼

github地址:https://github.com/dchack/Mybatis-source-code-learn (歡迎star)java

TransactionFactory

官方文檔:mysql

在 MyBatis 中有兩種類型的事務管理器(也就是 type=」[JDBC|MANAGED]」):
JDBC – 這個配置就是直接使用了 JDBC 的提交和回滾設置,它依賴於從數據源獲得的鏈接來管理事務做用域。
MANAGED – 這個配置幾乎沒作什麼。它歷來不提交或回滾一個鏈接,而是讓容器來管理事務的整個生命週期(好比 JEE 應用服務器的上下文)。 默認狀況下它會關閉鏈接,然而一些容器並不但願這樣,所以須要將 closeConnection 屬性設置爲 false 來阻止它默認的關閉行爲。例如:git

<transactionManager type="MANAGED">
 <property name="closeConnection" value="false"/>
</transactionManager>

提示若是你正在使用 Spring + MyBatis,則沒有必要配置事務管理器, 由於 Spring 模塊會使用自帶的管理器來覆蓋前面的配置。github

以上配置transactionManager屬性來配置使用哪種TransactionFactory的代碼,確定在MybatisXMLConfigBuilder中能夠找到:spring

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.");
    }

TransactionFactory入手:sql

public interface TransactionFactory {

  /**
   * Sets transaction factory custom properties.
   * @param props
   */
  void setProperties(Properties props);

  /**
   * Creates a {@link Transaction} out of an existing connection.
   * @param conn Existing database connection
   * @return Transaction
   * @since 3.1.0
   */
  Transaction newTransaction(Connection conn);
  
  /**
   * Creates a {@link Transaction} out of a datasource.
   * @param dataSource DataSource to take the connection from
   * @param level Desired isolation level
   * @param autoCommit Desired autocommit
   * @return Transaction
   * @since 3.1.0
   */
  Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);

}

TransactionFactory接口描述實現者須要從ConnectionDataSource生產org.apache.ibatis.transaction.Transaction出來。apache

接口實現類以下:
類關係圖服務器

他們各自生產的Transaction分別是:ide

  • JdbcTransaction
  • ManagedTransaction
  • SpringManagedTransaction

Transaction接口:ui

/**
 * Wraps a database connection.
 * Handles the connection lifecycle that comprises: its creation, preparation, commit/rollback and close. 
 *
 * @author Clinton Begin
 */
public interface Transaction {

  /**
   * 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;
  
}

抽象出了控制connection生命週期的核心接口:getConnection(create),commit,rollback,close

JdbcTransaction的實現:

三個操做方法:commit,rollback,close,都是connection的封裝而已,commit,rollback執行的條件須要已經生成好connection而且AutoCommit沒有設置true,close方法會調用resetAutoCommit方法重置ConnectionautoCommit屬性爲true

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

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

  @Override
  public void close() throws SQLException {
    if (connection != null) {
      resetAutoCommit();
      if (log.isDebugEnabled()) {
        log.debug("Closing JDBC Connection [" + connection + "]");
      }
      connection.close();
    }
  }

重置autoCommit屬性方法:

protected void resetAutoCommit() {
    try {
      if (!connection.getAutoCommit()) {
        // MyBatis does not call commit/rollback on a connection if just selects were performed.
        // Some databases start transactions with select statements
        // and they mandate a commit/rollback before closing the connection.
        // A workaround is setting the autocommit to true before closing the connection.
        // Sybase throws an exception here.
        if (log.isDebugEnabled()) {
          log.debug("Resetting autocommit to true on JDBC Connection [" + connection + "]");
        }
        connection.setAutoCommit(true);
      }
    } catch (SQLException e) {
      if (log.isDebugEnabled()) {
        log.debug("Error resetting autocommit to true "
          + "before closing the connection.  Cause: " + e);
      }
    }
  }

在看下getConnection方法的實現:

@Override
  public Connection getConnection() throws SQLException {
    if (connection == null) {
      openConnection();
    }
    return connection;
  }
  protected void openConnection() throws SQLException {
    if (log.isDebugEnabled()) {
      log.debug("Opening JDBC Connection");
    }
    connection = dataSource.getConnection();
    if (level != null) {
      connection.setTransactionIsolation(level.getLevel());
    }
    setDesiredAutoCommit(autoCommmit);
  }

openConnection中設置了事務隔離級別(transaction isolation level)和autoCommmit。

事務隔離級別在TransactionIsolationLevel枚舉中能夠看到:

public enum TransactionIsolationLevel {
  NONE(Connection.TRANSACTION_NONE),
  READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED),
  READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED),
  REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),
  SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE);

  private final int level;

  private TransactionIsolationLevel(int level) {
    this.level = level;
  }

  public int getLevel() {
    return level;
  }
}

java.sql.Connection中的定義和註釋以下:

/**
     * A constant indicating that transactions are not supported.
     */
    int TRANSACTION_NONE             = 0;

    /**
     * A constant indicating that
     * dirty reads, non-repeatable reads and phantom reads can occur.
     * This level allows a row changed by one transaction to be read
     * by another transaction before any changes in that row have been
     * committed (a "dirty read").  If any of the changes are rolled back,
     * the second transaction will have retrieved an invalid row.
     */
    int TRANSACTION_READ_UNCOMMITTED = 1;

    /**
     * A constant indicating that
     * dirty reads are prevented; non-repeatable reads and phantom
     * reads can occur.  This level only prohibits a transaction
     * from reading a row with uncommitted changes in it.
     */
    int TRANSACTION_READ_COMMITTED   = 2;

    /**
     * A constant indicating that
     * dirty reads and non-repeatable reads are prevented; phantom
     * reads can occur.  This level prohibits a transaction from
     * reading a row with uncommitted changes in it, and it also
     * prohibits the situation where one transaction reads a row,
     * a second transaction alters the row, and the first transaction
     * rereads the row, getting different values the second time
     * (a "non-repeatable read").
     */
    int TRANSACTION_REPEATABLE_READ  = 4;

    /**
     * A constant indicating that
     * dirty reads, non-repeatable reads and phantom reads are prevented.
     * This level includes the prohibitions in
     * <code>TRANSACTION_REPEATABLE_READ</code> and further prohibits the
     * situation where one transaction reads all rows that satisfy
     * a <code>WHERE</code> condition, a second transaction inserts a row that
     * satisfies that <code>WHERE</code> condition, and the first transaction
     * rereads for the same condition, retrieving the additional
     * "phantom" row in the second read.
     */
    int TRANSACTION_SERIALIZABLE     = 8;
關於事務隔離級別

幾個概念:

  • 髒讀:讀取的數據能夠取到其餘未提交事務修改的數據
  • 不可重複讀:一個事務中屢次讀取相同的數據,因其餘事務在中間修改了這個數據,致使第一個事務屢次讀的數據會不相同
  • 幻讀:就是在一個事務提交時發現以前查的條件發生了改變

隔離級別:

  • 提交讀(READ_COMMITTED)只能讀取到已經提交的數據
  • 未提交讀(READ_UNCOMMITTED)容許髒讀
  • 可重複讀(REPEATABLE_READ)在同一事務中保證屢次讀取的數據是一致的
  • 串行讀(SERIALIZABLE)每次讀都須要獲取表級鎖,讀寫互相阻塞

mysql中查看隔離級別設置:

select @@global.tx_isolation;

另外咱們也看到JdbcTransaction中是須要autoCommmit設置true的,不然是不能完成事務功能的。

ManagedTransaction

從類註釋上能夠看到:ManagedTransaction是將事務的生命週期交給容器管理,能夠理解它都是空實現,好比commit,rollbackclose能夠經過closeConnection字段來關閉。

SpringManagedTransaction

後續進入Mybatis擴展模塊時展開。

相關文章
相關標籤/搜索