淺析Mybatis中的事物提交commit()方法

       Mybatis通過對JDBC進行封裝,極大地簡化了程序員對數據庫的操作,例如對數據庫的增刪改查操作。其中當進行增刪改操作時,都會涉及到用戶數據的提交,那麼Mybatis中究竟是如何完成數據的提交的呢?這裏將對Mybatis中的commit()方法進行簡單的剖析,看看底層是如何實現事物提交的。

       其實Mybatis中可以設置自動提交功能。在利用工廠模式獲得SqlSession實現類對象時,採用openSession(true),即可實現自動提交,無需調用commit()方法。但是程序是爲了現實中的應用場景而產生的,現實中往往採用手動提交的方式,避免誤操作,因此通常使用的是openSession()方法,底層獲得了一個SqlSession接口的實現類DefaultSqlSession,這是它的構造方法,這裏提一句,dirty=false表示該對象中的數據與數據庫同步,不是髒數據。

public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
}

       通過進入DefaultSqlSession類可以看到,我們進行增刪改操作,最終底層都會調用這個類中的update()方法,以下是update()方法:

public int update(String statement, Object parameter) {
    try {
      dirty = true;
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
}

       可以看到dirty=true,這裏將修改產生的數據暫時視爲髒數據,即未與數據庫同步。

    當程序執行到commit()方法,即手動提交數據時,其實調用的是DefaultSqlSession類中的commit(false)方法。

//commit()方法調用過程
public void commit() {
    commit(false);
  }

public void commit(boolean force) {
    try {
      executor.commit(isCommitOrRollbackRequired(force));
      dirty = false;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
}
      這個方法的核心是執行器executor調用了commit()方法,先來看看方法內部的參數isCommitOrRollbackRequired(force)方法,這裏爲或表達式,左邊爲true直接短路返回true。

private boolean isCommitOrRollbackRequired(boolean force) {
    return (!autoCommit && dirty) || force;
}
public void commit(boolean force) {
    try {
      executor.commit(isCommitOrRollbackRequired(force));
      dirty = false;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
}

      因此執行器執行的方法是commit(true),執行完之後將dirty重新改爲false,因爲數據已經與數據庫同步,不再是髒數據。通過調試可以知道,這裏的executor是Executor接口實現類CachingExecutor的對象:

public void commit(boolean required) throws SQLException {
    delegate.commit(required);
    tcm.commit();
}

而CachingExecutor可以看成是SimpleExecutor類的一個裝飾類,主要作用是在SimpleExecutor加一個緩衝區。因此delegate實際上SimpleExecutor類的對象,調用的自然是本類的commit()方法:

public void commit(boolean required) throws SQLException {
    if (closed) throw new ExecutorException("Cannot commit, transaction is already closed");
    clearLocalCache();
    flushStatements();
    if (required) {
      transaction.commit();
    }
}

      這裏首先進行了清空緩存和刷新數據,然後進行的事物提交,即transaction.commit(),這裏的transaction是Transaction接口實現類JdbcTransaction的對象,進入到這個類中,

public void commit() throws SQLException {
    if (connection != null && !connection.getAutoCommit()) {
      if (log.isDebugEnabled()) {
        log.debug("Committing JDBC Connection [" + connection + "]");
      }
      connection.commit();
    }
}
可以看到底層調用的依然是java基礎api中的Connection中的commit方法,這也再一次證明了Mybatis框架實際上是對Jdbc的封裝。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章