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的封裝。