欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Mybatis 源码阅读----事务模块(Transaction)

程序员文章站 2022-07-12 22:40:09
...

该章将介绍Mybatis对事务的封装,事务模块所在的包位置如下:

Mybatis 源码阅读----事务模块(Transaction)
各个类之间的关系图如下:
Mybatis 源码阅读----事务模块(Transaction)

1、Transaction接口如下

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;

}

2、其实现类JdbcTransaction实现Transaction接口其中定义字段如下

 /**
   *  connection 对象
   */
  protected Connection connection;

  /**
   *  数据源
   */
  protected DataSource dataSource;

  /**
   * 数据库隔离级别
   */
  protected TransactionIsolationLevel level;

  /**
   *  是否自动提交
   */
  protected boolean autoCommit;

在JdbcTransaction的构造函数中首先会初始化connection之外的三个字段,而connection是通过getConnection() 方法延时初始化化。
构造器如下:

 public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
    dataSource = ds;
    level = desiredLevel;
    autoCommit = desiredAutoCommit;
  }

getConnection()方法如下

public Connection getConnection() throws SQLException {
    // 如果connection为空,则创建
    if (connection == null) {
      openConnection();
    }
    return connection;
  }

openConnection()方法主要是从数据源中获取连接并设置相关属性,如下

protected void openConnection() throws SQLException {
    if (log.isDebugEnabled()) {
      log.debug("Opening JDBC Connection");
    }
    // 从数据源中获取连接
    connection = dataSource.getConnection();
    if (level != null) {
      // 设置数据路隔离级别
      connection.setTransactionIsolation(level.getLevel());
    }
    // 设置autoCommit 属性
    setDesiredAutoCommit(autoCommit);
  }

setDesiredAutoCommit() 方法如下

protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
    try {
      // 将事务提交方式设置为 desiredAutoCommit
      if (connection.getAutoCommit() != desiredAutoCommit) {
        if (log.isDebugEnabled()) {
          log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + connection + "]");
        }
        connection.setAutoCommit(desiredAutoCommit);
      }
    } catch (SQLException e) {
      // Only a very poorly implemented driver would fail here,
      // and there's not much we can do about that.
      throw new TransactionException("Error configuring AutoCommit.  "
          + "Your driver may not support getAutoCommit() or setAutoCommit(). "
          + "Requested setting: " + desiredAutoCommit + ".  Cause: " + e, e);
    }
  }

事务提交方法如下:

public void commit() throws SQLException {
    // connection 不为 null  并且 connection不是自动提交
    if (connection != null && !connection.getAutoCommit()) {
      if (log.isDebugEnabled()) {
        log.debug("Committing JDBC Connection [" + connection + "]");
      }
      // 手动提交事务
      connection.commit();
    }
  }

事务回滚的方法如下:

public void rollback() throws SQLException {
    // 非自动提交,则进行回滚
    if (connection != null && !connection.getAutoCommit()) {
      if (log.isDebugEnabled()) {
        log.debug("Rolling back JDBC Connection [" + connection + "]");
      }
      connection.rollback();
    }
  }

关闭connection链接方法如下:

 public void close() throws SQLException {
    if (connection != null) {
      // 将connection重置为自动提交
      resetAutoCommit();
      if (log.isDebugEnabled()) {
        log.debug("Closing JDBC Connection [" + connection + "]");
      }
      // 关闭connection
      connection.close();
    }
  }

resetAutoCommit()方法如下:

protected void resetAutoCommit() {
    try {
      if (!connection.getAutoCommit()) {
       // MyBatis does not call commit/rollback on a connection if just selects were performed.
       // 当执行查询时,mybatis不需要提交或者回滚
        // 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);
      }
    }
  }

3.ManagedTransaction 的实现更加简单,它同样依赖其中的 dataSource 宇段获取连接, 但其commit()、 rollback()方法都是空实现,事务的提交和回滚都是依靠容器管理的。Managed Transaction 中通过 closeConnection 字段的值控制数据库连接 关闭行为。比较简单可自行看一下源码

4.TranscationFactory定义如下:

public interface TransactionFactory {

  /**
   * 设置工厂属性(运用了java8中的新特性,在接口中允许default方法)
   * Sets transaction factory custom properties.
   * @param props
   */
  default void setProperties(Properties props) {
    // NOP
  }

  /**
   *
   * 创建transaction
   * 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的实现类为JdbcTransactionFactory和ManagedTransactionFactory,主要是为了创建JdbcTranscation 和 ManagedTransaction,比较简单,就不贴出来代码了。至此Transaction模块分析完毕。

参考文献 《Mybatis技术内幕》