概述
執行器包主要包含了 Executor、ParameterHandler、ResultSetHandler、StatementHandler。
這些都是sql執行中非常重要的一環,本篇從Executor開始。
Executor:執行器,主要職責是在sql執行過程中添加緩存和事務的功能。與jdbc相關的操作會繼續委託給StatementHandler。
Executor
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
// 更新
int update(MappedStatement ms, Object parameter) throws SQLException;
// 查詢 resultHandler + cacheKey + boundSql
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
// 查詢 resultHandler
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
// 查詢遊標
<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
// 刷入批處理
List<BatchResult> flushStatements() throws SQLException;
// 提交事務
void commit(boolean required) throws SQLException;
// 回滾事務
void rollback(boolean required) throws SQLException;
// 創建緩存鍵
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
// 是否緩存
boolean isCached(MappedStatement ms, CacheKey key);
// 清除緩存
void clearLocalCache();
// 延遲加載
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
// 獲取事務
Transaction getTransaction();
// 關閉事務
void close(boolean forceRollback);
// 是否關閉
boolean isClosed();
// 設置執行器包裝類
void setExecutorWrapper(Executor executor);
}
將Executor提供的方法分類
- sql操作
- 緩存
- 事務
可得出Executor的基本職責。
Executor的實現類:
- BaseExecutor:基礎執行器,實現Executor方法,添加通用緩存、事務處理,模板方法模式。
- CachingExecutor:緩存執行器,BaseExecutor內部包含的是一級緩存,CachingExecutor是二級緩存。
- ReuseExecutor:可重用執行器,會將Statement緩存。
- SimpleExecutor:基礎執行器,值也包含默認數據庫執行。
- BatchExecutor:批處理執行器,可進行批量執行sql(僅限Update)。
BaseExecutor
public abstract class BaseExecutor implements Executor {
private static final Log log = LogFactory.getLog(BaseExecutor.class);
/**
* 事務
*/
protected Transaction transaction;
/**
* 包裝器
*/
protected Executor wrapper;
/**
* 延遲加載
*/
protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
/**
* 一級緩存
*/
protected PerpetualCache localCache;
/**
* 輸出參數緩存
*/
protected PerpetualCache localOutputParameterCache;
/**
* 配置
*/
protected Configuration configuration;
/**
* 查詢的深度
*/
protected int queryStack;
/**
* 是否關閉
*/
private boolean closed;
}
update
@Override
public int update(MappedStatement ms, Object parameter) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
// 關閉狀態校驗
if (closed) {
throw new ExecutorException("Executor was closed.");
}
// 清除緩存
clearLocalCache();
return doUpdate(ms, parameter);
}
query
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
/*
* 獲取真正的sql,將入參傳入,佔位符替換
*/
BoundSql boundSql = ms.getBoundSql(parameter);
/*
* 創建一級緩存key
*/
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
/*
* 先嚐試從緩存拿
*/
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
// 存儲過程時,緩存outPut類型參數
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
/*
* 實時查庫
*/
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
// 延遲加載處理
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
// 緩存是Statement級別,則清楚
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
// 返回實體
return list;
}
commit
@Override
public void commit(boolean required) throws SQLException {
if (closed) {
throw new ExecutorException("Cannot commit, transaction is already closed");
}
// 清除緩存
clearLocalCache();
// 刷新statement
flushStatements();
if (required) {
transaction.commit();
}
}
rollback
@Override
public void rollback(boolean required) throws SQLException {
if (!closed) {
try {
clearLocalCache();
flushStatements(true);
} finally {
if (required) {
transaction.rollback();
}
}
}
}
SimpleExecutor
簡單執行器,最常用的執行器。
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 創建 statementHandler
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
// 創建statement實例
stmt = prepareStatement(handler, ms.getStatementLog());
// 執行
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
這裏步驟很簡單,創建StatementHandler和Statement實例,執行sql交給StatementHandler。
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
/*
* 創建一個statement處理器 用於操作jdbc statement
*/
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
/*
* 根據statementType創建對應的statement實例
* simple
* prepare
* callable
*/
stmt = prepareStatement(handler, ms.getStatementLog());
/*
* 執行sql並轉換返回值
* 這裏handler類型是RoutingStatementHandler
* 根據org.apache.ibatis.mapping.MappedStatement.statementType
* 路由到真正的StatementHandler
*/
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
query和update邏輯相同。
創建Statement實例
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
// 獲取數據源連接,這裏會對Connection創建代理類,添加了日誌打印的功能
Connection connection = getConnection(statementLog);
//獲取一個Statement實例
stmt = handler.prepare(connection, transaction.getTimeout());
//往statement裏設置參數
handler.parameterize(stmt);
return stmt;
}
ReuseExecutor
可複用Statement實例執行器。SimpleExecutor中Statement是一次性的,用完馬上關閉。
private final Map<String, Statement> statementMap = new HashMap<>();
存儲Statement緩存,key是 動態sql。其他與SimpleExecutor完全相同。
BatchExecutor
批量執行器,可批量執行sql。僅限update(jdbc僅支持批量update)
實在用的少,略過。
CachingExecutor
緩存執行器,是mybatis二級緩存機制的實現類。
public class CachingExecutor implements Executor {
private final Executor delegate;
/**
* 緩存跨SqlSession,所以需要事務控制
*/
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
}
這裏也是典型的裝飾模式,執行器的具體實現都委託給Delegate執行。自己在之上添加緩存功能。
這裏主要看下query方法,看下緩存的處理
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
// 判斷緩存容器是否存在
if (cache != null) {
// 是否需要刷新緩存
flushCacheIfRequired(ms);
// 是否使用緩存
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
// 讀取二級緩存值(受事務管理)
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
// 二級緩存爲null,委託實例查詢
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
// 設置值到二級緩存
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
// 沒有緩存容器,說明沒有二級緩存
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
從代碼中可以看出,mybatis的緩存機制順序
- 二級緩存
- 一級緩存
- 數據庫
二級緩存在如今分佈式環境下,也使用的非常少。
總結
executor是介於SqlSession和StatementHandler之間的,用於專門處理緩存和事務的。
不對外直接開放,也不與jdbc直接交互。在我們應用開發中,也可以細化dao層,添加緩存事務的這一層。