Mybatis事務管理


一、Mybatis事務

1、事務管理方式

Mybatis中的事務管理方式有兩種:

1、JDBC的事務管理機制,即使用JDBC事務管理機制進行事務管理

2、MANAGED的事務管理機制,Mybatis沒有實現對事務的管理,而是通過容器來實現對事務的管理

其中,Mybatis提供了事務的接口: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
   */
  //獲取事務timeout
  Integer getTimeout() throws SQLException;
  
}

Transaction有兩個實現類:JdbcTransaction和ManagedTransaction,分別對應兩種事務管理方式。

JdbcTransaction的代碼如下:

public class JdbcTransaction implements Transaction {

  private static final Log log = LogFactory.getLog(JdbcTransaction.class);

  //數據連接
  protected Connection connection;
  //數據源
  protected DataSource dataSource;
  //事務等級
  protected TransactionIsolationLevel level;
  //事務提交
  protected boolean autoCommmit;

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

  public JdbcTransaction(Connection connection) {
    this.connection = connection;
  }

  @Override
  public Connection getConnection() throws SQLException {
    if (connection == null) {
      openConnection();
    }
    return connection;
  }

  @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();
    }
  }

  protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
    try {
      //事務提交狀態不一致時修改
      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);
    }
  }

  protected void resetAutoCommit() {
    try {
      if (!connection.getAutoCommit()) {
        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);
      }
    }
  }

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

  @Override
  public Integer getTimeout() throws SQLException {
    return null;
  }
}

JdbcTransaction通過使用jdbc提供的方式來管理事務,通過Connection提供的事務管理方法來進行事務管理,只是將JDBC的事務管理進行了封裝。

ManagedTransaction實現類是通過容器來進行事務管理,所有它對事務提交和回滾並不會做任何操作。其代碼如下:

public class ManagedTransaction implements Transaction {

  private static final Log log = LogFactory.getLog(ManagedTransaction.class);

  private DataSource dataSource;
  private TransactionIsolationLevel level;
  private Connection connection;
  private final boolean closeConnection;

  public ManagedTransaction(Connection connection, boolean closeConnection) {
    this.connection = connection;
    this.closeConnection = closeConnection;
  }

  public ManagedTransaction(DataSource ds, TransactionIsolationLevel level, boolean closeConnection) {
    this.dataSource = ds;
    this.level = level;
    this.closeConnection = closeConnection;
  }

  @Override
  public Connection getConnection() throws SQLException {
    if (this.connection == null) {
      openConnection();
    }
    return this.connection;
  }

  @Override
  public void commit() throws SQLException {
    // Does nothing
  }

  @Override
  public void rollback() throws SQLException {
    // Does nothing
  }

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

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

  @Override
  public Integer getTimeout() throws SQLException {
    return null;
  }

}

此外,當Mybatis與Spring一起使用時,Spring會提供一個Transaction的實現類SpringManagedTransaction進行事務管理,這會在后面的Spring源碼中說到。

可參考:https://blog.csdn.net/qq924862077/article/details/525997851.2

2、事務配置方式

Mybatis的事務管理方式的配置是在核心配置文件中進行的,它是在configuration標簽下的environments中與數據源一起配置的,可以配置多個,如下所示:

 <environments default="development">
    <environment id="development">
      //配置事務管理方式
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
      </dataSource>
    </environment>
</environments>

二、事務隔離級別

在上面的源碼中,我們看到TransactionIsolationLevel—事務隔離級別,Mybaais中定義了五種隔離級別,代碼如下所示:

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

其中四種是一般數據庫的事務隔離級別,從高到底以此為:Read uncommitted(讀未提交)、Read committed(讀已提交)、Repeatable read(可重復讀)、Serializable(可串行化),這幾個級別主要用於解決臟讀、不可重復讀、幻讀等問題,其總結如下表:

級別 臟讀 不可重復讀 幻讀 說明
讀未提交 Y Y Y 一個更新語句沒有提交,但是別的事務可以讀到這個改變.這是很不安全的。允許任務讀取數據庫中未提交的數據更改,也稱為臟讀。
讀已提交 X Y Y 語句提交以后即執行了COMMIT以后別的事務就能讀到這個改變. 只能讀取到已經提交的數據。Oracle等多數數據庫默認都是該級別,可防止臟讀。
可重復讀 X X Y 同一個事務里面先后執行同一個查詢語句的時候,得到的結果是一樣的.在同一個事務內的查詢都是事務開始時刻一致的,InnoDB默認級別。在SQL標准中,該隔離級別消除了不可重復讀,但是還存在幻象讀。
可串行化 X X X 事務執行的時候不允許別的事務並發執行. 完全串行化的讀,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞。

注:Y表示出現,X表示不出現

參考自:https://blog.csdn.net/qq924862077/article/details/52599961

Mybatis添加的隔離級別NONE,可在DefaultSqlSessionFactory中創建SqlSession時,設置數據庫的事務隔離級別,以及通過設置autoCommit來設置事務的提交方式。代碼參見DefaultSqlSessionFactory,示例代碼如下:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit)

三、事務工廠

此外,在Mybatis中還提供了事務工廠:TransactionFactory,代碼如下所示:

public interface TransactionFactory {

  //配置工廠屬性
  void setProperties(Properties props);

  //通過Connection獲取事務
  Transaction newTransaction(Connection conn);
  
  //通過數據庫,事務等級,是否自動提交創建事務
  Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);

}

類似的,TransactionFactory也有兩個實現類:JdbcTransactionFactory和ManagedTransactionFactory

JdbcTransactionFactory代碼如下:

public class JdbcTransactionFactory implements TransactionFactory {

  @Override
  public void setProperties(Properties props) {
  }

  @Override
  public Transaction newTransaction(Connection conn) {
    return new JdbcTransaction(conn);
  }

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

ManagedTransactionFactory代碼如下:

public class ManagedTransactionFactory implements TransactionFactory {

  private boolean closeConnection = true;

  @Override
  public void setProperties(Properties props) {
    if (props != null) {
      String closeConnectionProperty = props.getProperty("closeConnection");
      if (closeConnectionProperty != null) {
        closeConnection = Boolean.valueOf(closeConnectionProperty);
      }
    }
  }

  @Override
  public Transaction newTransaction(Connection conn) {
    return new ManagedTransaction(conn, closeConnection);
  }

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM