1. 程式人生 > >mybatis事務管理機制詳解

mybatis事務管理機制詳解

attribute log builder tween gen 事務管理 getc there spro

1.mybatis事務的配置和使用

mybatis事務有兩種使用方式:

(a):使用JDBC的事務管理機制:即使用java.Sql.Connection對象完成對事務的提交,回滾和關閉操作。

(b):使用MANAGED的事務管理機制:mybatis本身不會去實現事務管理的相關操作,而是交個外部容器來管理事務

在配置文件中的配置:

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

在這段配置信息中,有三個節點:<transactionManager />,<dataSource>,<environment>。其中,<dataSource>節點封裝了數據源的信息,<environment>封裝了數據庫的連接,包括數據源和事務,然後把該節點信息封裝到了Configuration對象中,方便後面使用,現在看下mybatis在初始化加載配置文件時,對<environment/>節點的解析:

  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)) {
// 解析 <transactionManager>節點 進入該方法
TransactionFactory txFactory
= transactionManagerElement(child.evalNode("transactionManager"));
// 解析<DataSource>節點 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()); } } } }

進入解析<transactionManager/>節點的方法:

  private TransactionFactory transactionManagerElement(XNode context) throws Exception {
    if (context != null) {
// 獲取type的屬性值,JDBC或者MANAGED String type
= context.getStringAttribute("type"); Properties props = context.getChildrenAsProperties();
//生成一個TransactionFactory實例 TransactionFactory factory
= (TransactionFactory) resolveClass(type).newInstance(); factory.setProperties(props); return factory; } throw new BuilderException("Environment declaration requires a TransactionFactory."); }

2.事務工廠TransactionFactory

TransactionFactory定義了創建Transaction的兩種方法,如下:

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
* 方法一:通過指定的Connection對象來創建事務
*/ 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
* 方法二:通過DataSource,隔離級別,是否自動提交 來創建事務
*/ Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit); }

TransactionFactory有兩個子類:JdbcTransactionFactory和ManagedTransactionFactory

2.1 JdbcTransactionFactory,會創建JDBC類型的事務,就是JdbcTransaction,看下源碼:

public class JdbcTransactionFactory implements TransactionFactory {

  public void setProperties(Properties props) {
  }
  // JDBCTransaction的創建有兩種方法  
  public Transaction newTransaction(Connection conn) {
    return new JdbcTransaction(conn);
  }

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

2.2 ManagedTransactionFactory,會創建MANAGED類型的事務,ManagedTransaction,同樣有兩種方法:

public class ManagedTransactionFactory implements TransactionFactory {

  private boolean closeConnection = true;

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

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

  public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
    // Silently ignores autocommit and isolation level, as managed transactions are entirely
    // controlled by an external manager.  It‘s silently ignored so that
    // code remains portable between managed and unmanaged configurations.
    return new ManagedTransaction(ds, level, closeConnection);
  }
}

3.JdbcTransaction和ManagedTransaction

3.1 JdbcTransaction

從源碼可知:jdbcTransaction從DataSource獲取連接對象Connection,然後利用Connection對象管理事務的commit和rollback,實際在事務處理上,jdbcTransaction是對java.sql.Connection的一個包裝,它是使用Connection對象來管理事務的。

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對象提交 public void commit() throws SQLException { 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(); } } 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()) { // 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) { 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); } }

3.2 ManagedTransaction

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

mybatis事務管理機制詳解