摘要:簡介上一篇文章源碼解析一初始化和動態(tài)代理分析了解析配置文件以及動態(tài)代理相關(guān)的源碼,這一篇接著上一篇探究的執(zhí)行流程,另外了解一下中的緩存。總結(jié)本文主要分析了的執(zhí)行流程,結(jié)合上一篇文章基本了解了的運(yùn)行原理。
簡介
上一篇文章(MyBatis 源碼解析(一):初始化和動態(tài)代理)分析了 MyBatis 解析配置文件以及 Mapper 動態(tài)代理相關(guān)的源碼,這一篇接著上一篇探究 SqlSession 的執(zhí)行流程,另外了解一下 MyBatis 中的緩存。
openSessionMyBatis 在解析完配置文件后生成了一個 DefaultSqlSessionFactory 對象,后續(xù)執(zhí)行 SQL 請求的時候都是調(diào)用其 openSession 方法獲得 SqlSessison,相當(dāng)于一個 SQL 會話。 SqlSession 提供了操作數(shù)據(jù)庫的一些方法,如 select、update 等。
先看一下 DefaultSqlSessionFactory 的 openSession 的代碼:
public SqlSession openSession() { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); } private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { // 從 configuration 取出配置 final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); // 每個 SqlSession 都有一個多帶帶的 Executor 對象 final Executor executor = configuration.newExecutor(tx, execType, autoCommit); // 返回 DefaultSqlSession 對象 return new DefaultSqlSession(configuration, executor); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
主要代碼在 openSessionFromDataSource,首先是從 Configuration 中取出相關(guān)的配置,生成 Transaction,接著又創(chuàng)建了一個 Executor,最后返回了 DefaultSqlSession 對象。
這里的 Executor 是什么呢?它其實是一個執(zhí)行器,SqlSession 的操作會交給 Executor 去執(zhí)行。MyBatis 的 Executor 常用的有以下幾種:
SimpleExecutor: 默認(rèn)的 Executor,每個 SQL 執(zhí)行時都會創(chuàng)建新的 Statement
ResuseExecutor: 相同的 SQL 會復(fù)用 Statement
BatchExecutor: 用于批處理的 Executor
CachingExecutor: 可緩存數(shù)據(jù)的 Executor,用代理模式包裝了其它類型的 Executor
了解了 Executor 的類型后,看一下 configuration.newExecutor(tx, execType, autoCommit) 的代碼:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) { // 默認(rèn)是 SimpleExecutor executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } // 默認(rèn)啟動緩存 if (cacheEnabled) { executor = new CachingExecutor(executor); } executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
MyBatis 默認(rèn)啟用一級緩存,即同一個 SqlSession 會共用同一個緩存,上面代碼最終返回的是 CachingExecutor。
getMapper在創(chuàng)建了 SqlSession 之后,下一步是生成 Mapper 接口的代理類,代碼如下:
publicT getMapper(Class type) { return configuration. getMapper(type, this); }
可以看出是從 configuration 中取得 Mapper,最終調(diào)用了 MapperProxyFactory 的 newInstance。MapperProxyFactory 在上一篇文章已經(jīng)分析過,它是為了給 Mapper 接口生成代理類,其中關(guān)鍵的攔截邏輯在 MapperProxy 中,下面是其 invoke 方法:
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { // 過濾一些不需要被代理的方法 if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else if (isDefaultMethod(method)) { return invokeDefaultMethod(proxy, method, args); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } // 從緩存中獲取 MapperMethod 然后調(diào)用 final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); }
MapperProxy 中調(diào)用了 MapperMethod 的 execute,下面是部分代碼:
public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { case INSERT: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); break; } case UPDATE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); break; } case DELETE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); break; } case SELECT: if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; ... }
可以看出,最終調(diào)用了 SqlSession 的對應(yīng)方法,也就是 DefaultSqlSession 中的方法。
select先看一下 DefaultSqlSession 中 select 的代碼:
public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) { try { MappedStatement ms = configuration.getMappedStatement(statement); executor.query(ms, wrapCollection(parameter), rowBounds, handler); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
select 中調(diào)用了 executor 的 query,上面提到,默認(rèn)的 Executor 是 CachingExecutor,看其中的代碼:
@Override publicList query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameterObject); // 獲取緩存的key CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } public List 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 list = (List ) tcm.getObject(cache, key); if (list == null) { list = delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } // 調(diào)用代理對象的緩存 return delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }
首先檢查緩存中是否有數(shù)據(jù),如果沒有再調(diào)用代理對象的 query,默認(rèn)是 SimpleExecutor。Executor 是一個接口,下面有個實現(xiàn)類是 BaseExecutor,其中實現(xiàn)了其它 Executor 通用的一些邏輯,包括 doQuery 以及 doUpdate 等,其中封裝了 JDBC 的相關(guān)操作。
updateupdate 的執(zhí)行與 select 類似, 都是從 CachingExecutor 開始,看代碼:
@Override public int update(MappedStatement ms, Object parameterObject) throws SQLException { // 檢查是否需要刷新緩存 flushCacheIfRequired(ms); // 調(diào)用代理類的 update return delegate.update(ms, parameterObject); }
update 會使得緩存的失效,所以第一步是檢查是否需要刷新緩存,接下來再交給代理類去執(zhí)行真正的數(shù)據(jù)庫更新操作。
總結(jié)本文主要分析了 SqlSession 的執(zhí)行流程,結(jié)合上一篇文章基本了解了 MyBatis 的運(yùn)行原理。對于 MyBatis 的源碼,還有很多地方?jīng)]有深入,例如SQL 解析時參數(shù)的處理、一級緩存與二級緩存的處理邏輯等,不過在熟悉 MyBatis 的整體框架之后,這些細(xì)節(jié)可以在需要用到的時候繼續(xù)學(xué)習(xí)。
如果我的文章對您有幫助,不妨點個贊支持一下(^_^)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/72760.html
摘要:我認(rèn)為學(xué)習(xí)框架源碼分為兩步抓住主線,掌握框架的原理和流程理解了處理思路之后,再去理解面向?qū)ο笏枷牒驮O(shè)計模式的用法目前第一步尚有問題,需要多走幾遍源碼,加深下理解,一起加油 這篇文章我們來深入閱讀下Mybatis的源碼,希望以后可以對底層框架不那么畏懼,學(xué)習(xí)框架設(shè)計中好的思想; 架構(gòu)原理 架構(gòu)圖 showImg(https://segmentfault.com/img/remote/...
摘要:最終解析出的和依然是設(shè)置到中。到這里,初始化部分就結(jié)束了。總結(jié)的初始化流程主要是解析配置文件,將相關(guān)信息保存在中,同時對每個代表的生成代理對象工廠。 簡介 MyBatis 是 Java 開發(fā)中非常流行的 ORM 框架,其封裝了 JDBC 并且解決了 Java 對象與輸入?yún)?shù)和結(jié)果集的映射,同時又能夠讓用戶方便地手寫 SQL 語句。MyBatis 的行為類似于以下幾行代碼: Class....
摘要:理解與掌握原理分析框架功能架構(gòu)接口層提供給外部使用的接口,開發(fā)人員通過這些本地來操作數(shù)據(jù)庫。流程分析數(shù)據(jù)處理過程根據(jù)的查找相應(yīng)的對象。預(yù)處理對象,得到對象。傳入和結(jié)果處理對象,通過的方法來執(zhí)行,并對執(zhí)行結(jié)果進(jìn)行處理。 MyBatis理解與掌握(原理分析) @(MyBatis)[Java, 框架, MyBatis] 功能架構(gòu) showImg(https://segmentfault.co...
摘要:目標(biāo)理清加載解析文件的過程理清執(zhí)行的過程。先看源碼生成解析配置文件考慮到項目的配置,看下生成和的代碼。在生成的過程中,使用了,這個類繼承了。在該類的構(gòu)造器中加載了和。下面看一下代碼從緩存中獲取對象執(zhí)行下面是方法至此,執(zhí)行完成。 目標(biāo): 理清mybatis加載解析mapper文件的過程; 理清mybatis執(zhí)行SQL的過程。 上一篇文章分析mybatis加載配置的源碼時提到了org....
摘要:一級緩存介紹及相關(guān)配置。在這個章節(jié),我們學(xué)習(xí)如何使用的一級緩存。一級緩存實驗配置完畢后,通過實驗的方式了解一級緩存的效果。源碼分析了解具體的工作流程后,我們隊查詢相關(guān)的核心類和一級緩存的源碼進(jìn)行走讀。 我,后端Java工程師,現(xiàn)在美團(tuán)點評工作。愛健身,愛技術(shù),也喜歡寫點文字。個人網(wǎng)站: http://kailuncen.me公眾號: KailunTalk (凱倫說) 前言 本文主要涉及...
閱讀 3495·2021-11-24 11:17
閱讀 2281·2021-11-15 11:38
閱讀 3367·2021-10-14 09:42
閱讀 2930·2019-08-30 15:54
閱讀 2024·2019-08-28 18:09
閱讀 539·2019-08-26 11:48
閱讀 1633·2019-08-26 10:48
閱讀 2147·2019-08-26 10:45