您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關(guān)什么是Mybatis Excuter框架,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
在BaseExecutor定義了Executor的基本實現(xiàn),如查詢一級緩存,事務(wù)處理等不變的部分,操作數(shù)據(jù)庫等變化部分由子類實現(xiàn),使用了模板設(shè)計模式,下面我們來看下查詢方法的源碼:
@Override public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameter); CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); return query(ms, parameter, rowBounds, resultHandler, key, boundSql); } /** * 所有的查詢操作最后都是由該方法來處理的 */ @SuppressWarnings("unchecked") @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) { // 處理存儲過程的OUT參數(shù) handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { // 緩存未命中,查詢數(shù)據(jù)庫 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { // 查詢層次減一 queryStack--; } if (queryStack == 0) { for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } // issue #601 deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { // issue #482 clearLocalCache(); } } return list; } private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; // 添加緩存占位符 localCache.putObject(key, EXECUTION_PLACEHOLDER); try { list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { // 刪除緩存占位符 localCache.removeObject(key); } // 將查詢結(jié)果添加到本地緩存 localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { // 如果是存儲過程則,緩存參數(shù) localOutputParameterCache.putObject(key, parameter); } return list; }
queryFromDatabase() 方法中,我們可以看到doQuery使用的是模板方法,具體邏輯是由子類來實現(xiàn)的,這樣做的好處是,子類只關(guān)心程序變化的部分,其他不變的部分由父類實現(xiàn)。提高了代碼的復(fù)用性和代碼的擴(kuò)展性。
普通的執(zhí)行器,Mybatis的默認(rèn)使用該執(zhí)行器,每次新建Statement。我們還是來看下查詢方法的源碼:
@Override public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { // 獲取Mybatis配置類 Configuration configuration = ms.getConfiguration(); // 根據(jù)配置類獲取StatementHandler StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); // 創(chuàng)建Statement stmt = prepareStatement(handler, ms.getStatementLog()); return handler.query(stmt, resultHandler); } finally { closeStatement(stmt); } } private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; // 獲取Connection連接 Connection connection = getConnection(statementLog); // 根據(jù)Connection獲取Statement stmt = handler.prepare(connection, transaction.getTimeout()); // 設(shè)置參數(shù) handler.parameterize(stmt); return stmt; }
通過
stmt = handler.prepare(connection, transaction.getTimeout());
方法我們可以看出每次是新建Statement
。
可以重用的執(zhí)行器,復(fù)用的是Statement,內(nèi)部以sql語句為key使用一個Map將Statement對象緩存起來,只要連接不斷開,那么Statement就可以重用。
因為每一個新的SqlSession都有一個新的Executor對象,所以我們緩存在ReuseExecutor上的Statement的作用域是同一個SqlSession,所以其實這個緩存用處其實并不大。我們直接看下獲取Statement
源碼,其他部分和SimpleExecutor
查詢方法一樣。
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; BoundSql boundSql = handler.getBoundSql(); String sql = boundSql.getSql(); if (hasStatementFor(sql)) { // 獲取復(fù)用的Statement stmt = getStatement(sql); applyTransactionTimeout(stmt); } else { // 新建Statement,并緩存 Connection connection = getConnection(statementLog); stmt = handler.prepare(connection, transaction.getTimeout()); putStatement(sql, stmt); } handler.parameterize(stmt); return stmt; } private boolean hasStatementFor(String sql) { try { // 根據(jù)sql判斷是否緩存了Statement,并判斷Connection是否關(guān)閉 return statementMap.keySet().contains(sql) && !statementMap.get(sql).getConnection().isClosed(); } catch (SQLException e) { return false; } } private Statement getStatement(String s) { return statementMap.get(s); } private void putStatement(String sql, Statement stmt) { statementMap.put(sql, stmt); }
批處理執(zhí)行器,通過封裝jdbc的 statement.addBatch(String sql) 以及 statement.executeBatch(); 來實現(xiàn)的批處理。該執(zhí)行器的事務(wù)只能是手動提交模式。
我們平時執(zhí)行批量的處理是一般還可以使用sql拼接的方式。
執(zhí)行批量更新時建議一次不要更新太多數(shù)據(jù),如果更新數(shù)據(jù)量比較大時可以分段執(zhí)行。
如果開啟了二級緩存那么Mybatis會使用CachingExecutor
執(zhí)行器,CachingExecutor
使用了裝飾器模式。
@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) { // 調(diào)用被裝飾則的方法 list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); // 將數(shù)據(jù)放入緩存 tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } // 沒找到緩存,直接調(diào)用被裝飾則的方法 return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
通過源碼我們發(fā)現(xiàn),整個查詢流程變成 了 L2 -> L1 -> DB。CachingExecutor
的查詢流程,增加了二級緩存的查詢操作。
我們在實際使用緩存過程中一般很少使用Mybatis的二級緩存,如果想做二級緩存,建議直接在service層面使用第三方緩存框架,推薦使用為監(jiān)控而生的多級緩存框架 layering-cache,使用更方便靈活,查詢流程是 L1 -> L2 -> DB。
BaseExecutor:使用了模板方法模式,定義了Executor的基本實現(xiàn),它是一個抽象類,不能直接對外提供服務(wù)。
SimpleExecutor:普通的執(zhí)行器,Mybatis的默認(rèn)使用該執(zhí)行器,每次新建Statement。
ReuseExecutor:可以重用Statement的執(zhí)行器,但是這個Statement緩存只在一次SqlSession中有效,我們平時生少有在一次SqlSession中進(jìn)行多次一樣的查詢操作,所以性能提升并不大。
BatchExecutor:批處理執(zhí)行器
CachingExecutor:二級緩存執(zhí)行器,使用裝飾器模式,整個查詢流程變成 了 L2 -> L1 -> DB。建議直接使用第三方緩存架,如:為監(jiān)控而生的多級緩存框架 layering-cache。
看完上述內(nèi)容,你們對什么是Mybatis Excuter框架有進(jìn)一步的了解嗎?如果還想了解更多知識或者相關(guān)內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。