1. 程式人生 > >MyBatis的事務管理和快取機制

MyBatis的事務管理和快取機制

一、MyBatis的事務管理

1.事務的概念:事務是一個或幾個操作組成的一個整體執行單元,它們要麼全部執行,要麼全不執行,不能只執行其中的某幾個操作;可以理解為一個事務是一個程式中執行的最小單元。

2.事務的特性:事務包含四個特性:原子性、一致性、隔離性、永續性,簡稱ACID性,具體詳解如下

        ·原子性:事務是應用中最小的執行單位,就像自然界中原子是最小的顆粒一樣,具有不可分隔的特性,事務是應用程式中不可分隔的最小邏輯執行單元。

        ·一致性:事務執行的結果,必須使資料庫從一種一致狀態,變成另為一種一致狀態。例如事務中包含兩條需要執行的sql語句,要麼兩條語句都執行成功,要麼都不成功,這就是一致性。

        ·隔離性:各個事務的執行互不干擾,任意一個事務的內部操作對其他併發的事務,都是隔離的。即,併發執行的事務之間是不會互相干擾的。

        ·持續性:指事務一旦提交,對資料的修改就是永久的,不可逆的。

3.事務的執行動作:資料庫的事務執行動作可分為:建立(create)、提交(commit)、回滾(rollback)、關閉(close),其對應的動作都抽象到了Transaction介面中,其原始碼如下:


public interface Transaction {

    //獲取資料庫連線
    java.sql.Connection getConnection() throws java.sql.SQLException;

    //提交
    void commit() throws java.sql.SQLException;

    //回滾
    void rollback() throws java.sql.SQLException;

    //關閉資料庫連線
    void close() throws java.sql.SQLException;
}

4.MyBatis的事務管理分類:

        ·使用JDBC的事務管理機制 —— 利用java.sql.Connnection物件完成對事務的提交、回滾和關閉等操作。

        ·使用MANAGED的事務管理機制 —— 對於這種機制,MyBatis自身不會去實現事務管理,而是讓容器如JBOSS等來實現對事務的管理。

5.事務的使用流程。

        5.1事務的配置,首先在MyBatis的根配置檔案mybatis-config.xml中定義如下資訊:

<environment id="mysql">
            <!--指定事務管理型別,type="JDBC"指直接使用JDBC的提交和回滾設定,type=“MANAGED”指讓容器實現對事務的管理-->
            <transactionManager type="JDBC" />
           <!-- <transactionManager type="MANAGED">
                <property name="closeConnection" value="false" />
            </transactionManager>-->
            <!-- dataSource資料來源配置,POOLED是JDBC連線物件的資料來源連線池的實現。-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
                <property name="username" value="root"/>
                <property name="password" value="123456" />
            </dataSource>
           
        </environment>

        5.2事務工廠的建立:MyBatis的事務建立是由org.apache.ibatis.transaction.TransactionFactory事務工廠來完成的,會根據<transactionManager>的type型別來建立是JdbcTransactionFactory工廠還是ManagedTransactionFactory工廠,其原始碼如下:

public interface TransactionFactory {
    void setProperties(java.util.Properties properties);

    org.apache.ibatis.transaction.Transaction newTransaction(java.sql.Connection connection);

    org.apache.ibatis.transaction.Transaction newTransaction(javax.sql.DataSource dataSource, org.apache.ibatis.session.TransactionIsolationLevel transactionIsolationLevel, boolean b);
}

         5.3 事務工廠TransactionFactory:通過TransactionFactory可以獲得到Transaction物件的例項,以JdbcTransaction為例,其原始碼如下:

public class JdbcTransactionFactory implements TransactionFactory {

  public void setProperties(Properties props) {
  }

  //根據給定的資料庫連線Connection建立Transaction  
  public Transaction newTransaction(Connection conn) {
    return new JdbcTransaction(conn);
  }

  //根據DataSource、隔離級別和是否自動提交建立Transaction
  public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
    return new JdbcTransaction(ds, level, autoCommit);
  }
}

        5.4 JdbcTransaction :JdbcTransaction可直接使用JDBC提交和回滾事務管理機制,JdbcTransaction是使用了java.sql.Connection上的commit和rollback功能來完成事務操作,其實JdbcTransaction只是把java.sql.Connection的事務處理進行了再次封裝,其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;
  }

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

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

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

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

}

          5.5 ManagedTransaction:ManagedTransaction讓容器來管理事務Transaction的整個生命週期,也就是說,使用ManagedTransaction的commit和rollback功能不會對事務有任何的影響,它什麼都不會做,它將事務管理的權利移交給了容器來實現,其原始碼如下:

public class ManagedTransaction implements Transaction {

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

  
  private DataSource dataSource;
  private TransactionIsolationLevel level;
  private Connection connection;
  private 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;
  }

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

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

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

  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("Openning JDBC Connection");
    }
    this.connection = this.dataSource.getConnection();
    if (this.level != null) {
      this.connection.setTransactionIsolation(this.level.getLevel());
    }
  }

}

二、MyBatis的快取機制

1、一級快取(SqlSession級別) ——

1.1 含義:在操作資料庫時需要建立SqlSession物件,在物件中有一個HashMap用來儲存快取資料,並且不同的SqlSession之間的快取資料區域是不會互相影響的。

1.2 作用域:一級快取的作用域是SqlSession範圍,就是當同一個SqlSession中執行兩次相同的sql查詢語句時,第一次回去資料庫中查詢資料並寫到快取中,第二次在查詢的時候,不會再去資料庫中去查詢,而是直接在快取中讀取資料。在使用時需要注意:當SqlSession執行DML操作(insert、update、delete)時,MyBatis會清空SqlSession中的一級快取,這樣做的目的是保證快取中的資料永遠是最新的資料,防止出現髒讀資料。

2.二級快取(mapper級別) ——

1.1 含義:使用二級快取時,多個SqlSession共享一個Mapper的sql語句去操作資料庫,得到的資料,同樣用HashMap來儲存快取資料,相比較於一級快取,二級快取的範圍更大,多個SqlSession共享二級快取,二級快取時跨SqlSession的。

1.2作用域:二級快取的作用域是mapper的同一個namespace,當不同的SqlSession執行相同的namespace下的sql語句,並向sql語句中傳遞的引數也相同時,第一次回去資料庫中查詢資料並寫到快取中,第二次在查詢的時候,不會再去資料庫中去查詢,而是直接在快取中讀取資料。

1.3 二級快取的使用配置:因為MyBatis預設沒有開啟二級快取,需要在setting全域性引數中開啟二級快取,其mybatis-config.xml配置檔案如下:

<settings>
     <!--開啟二級快取-->
    <setting name="cacheEnabled" value="true"/>
</settings>

在需要開啟二級快取的mapper檔案中加入如下配置:

<cache eviction="LRU" flushInterval="10000" size="512" readOnly="true" />

其上述元素配置詳解如下:

        ·eviction —— 收回策略,預設為LRU,一共有如下幾種回收策略

                ·LRU —— 最近最少使用的策略,移除最長時間不被使用的物件。

                ·FIFO —— 先進先出策略,按物件進入快取的順序來移除它們。

                ·SOFT —— 軟引用策略,移除基於垃圾回收器狀態和軟引用規則的物件。

                ·WEAK —— 弱引用策略,更積極地移除基於垃圾收集器狀態和弱引用規則的物件。

        ·flushInterval —— 重新整理間隔,可以被設定為任意的正整數,它的單位毫秒,預設情況下是不設定,也就是沒有重新整理間隔,快取僅僅呼叫語句時重新整理。

        ·size —— 快取數目,可以被設定為任意正整數,要記住你快取的物件數目和你執行環境的可用記憶體資源數目。預設值是1024。

        ·readOnly —— 只讀屬性,可以被設定為 true或false,只讀的快取會給所有呼叫著返回快取物件的相同例項,因此這些物件不能被修改,可讀寫的快取會返回快取物件的拷貝(通過序列化)。這樣做會慢一些,但是安全,因此預設是false。