国产成人精品久久免费动漫-国产成人精品天堂-国产成人精品区在线观看-国产成人精品日本-a级毛片无码免费真人-a级毛片毛片免费观看久潮喷

您的位置:首頁技術(shù)文章
文章詳情頁

淺談Mybatis SqlSession執(zhí)行流程

瀏覽:3日期:2023-10-18 17:44:11
目錄Mybatis執(zhí)行SQL流程SqlSessionExecutorMybatis之ExecutorMybatis之StatementHandler進(jìn)入ResultSetHandlerMybatis執(zhí)行SQL流程

在看源碼之前,我們需要了解一些基本知識,如果您沒有閱讀Mybatis SqlSessionFactory 初始化原理,可以先閱讀Mybatis SqlSessionFactory 初始化原理這篇文章,這用更有助于我們理解接下來的文章在看源碼之前,我們需要了解一些基本知識

SqlSession

SqlSession是一個接口,它有兩個實現(xiàn)類: - DefaultSqlSession:默認(rèn)實現(xiàn)類 - SqlSessionManager:已經(jīng)棄用的實現(xiàn)類,所以我們不需要關(guān)注他SqlSession是與數(shù)據(jù)庫交互的頂層類,通常與ThreadLocal綁定,一個會話使用一個SqlSession,SqlSession是線程不安全的,使用完畢需要close()

public class DefaultSqlSession implements SqlSession { private final Configuration configuration; private final Executor executor;}

SqlSession中最重要的兩個變量: - Configuration:核心配置類,也是初始化時傳過來的 - Executor:實際執(zhí)行SQL的執(zhí)行器

Executor

Executor是一個接口,有三個實現(xiàn)類 - BatchExecutor 重用語句,并執(zhí)行批量更新 - ReuseExecutor 重用預(yù)處理語句prepared statements - SimpleExecutor 普通的執(zhí)行器,默認(rèn)使用

了解完基本知識后,我們接著往下看代碼。

當(dāng)創(chuàng)建完SqlSessionFactory后,就可以創(chuàng)建SqlSession,然后使用SqlSession進(jìn)行增刪改查:

// 1. 讀取配置文件,讀成字節(jié)輸入流,注意:現(xiàn)在還沒解析InputStream resourceAsStream = Resources.getResourceAsStream('sqlMapConfig.xml');// 2. 解析配置文件,封裝Configuration對象 創(chuàng)建DefaultSqlSessionFactory對象SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();List<Object> objects = sqlSession.selectList('namespace.id');

我們先去看openSession()方法,創(chuàng)建了SqlSession

//6. 進(jìn)入openSession方法@Overridepublic SqlSession openSession() { //getDefaultExecutorType()傳遞的是SimpleExecutor // level:數(shù)據(jù)庫事物級別,null return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);}//7. 進(jìn)入openSessionFromDataSource。//ExecutorType 為Executor的類型,TransactionIsolationLevel為事務(wù)隔離級別,autoCommit是否開啟事務(wù)//openSession的多個重載方法可以指定獲得的SeqSession的Executor類型和事務(wù)的處理private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try {// 獲得 Environment 對象final Environment environment = configuration.getEnvironment();// 創(chuàng)建 Transaction 對象final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);// 創(chuàng)建 Executor 對象final Executor executor = configuration.newExecutor(tx, execType);// 創(chuàng)建 DefaultSqlSession 對象return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) {// 如果發(fā)生異常,則關(guān)閉 Transaction 對象closeTransaction(tx); // may have fetched a connection so lets call close()throw ExceptionFactory.wrapException('Error opening session. Cause: ' + e, e); } finally {ErrorContext.instance().reset(); }}

通過源碼可以清晰的看到,會話工廠創(chuàng)建了Environment,Transaction,Executor,DefaultSqlSession對象,并且對于會話對象來說,他的autoCommit默認(rèn)為false,默認(rèn)不自動提交。

然后我回到原來的代碼,接著就需要使用SqlSession進(jìn)行增刪改查操作了

所以我們進(jìn)入selectList()查看

//8.進(jìn)入selectList方法,多個重載方法@Overridepublic <E> List<E> selectList(String statement) { return this.selectList(statement, null);}@Overridepublic <E> List<E> selectList(String statement, Object parameter) { return this.selectList(statement, parameter, RowBounds.DEFAULT);}@Overridepublic <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try {// 獲得 MappedStatement 對象MappedStatement ms = configuration.getMappedStatement(statement);// 執(zhí)行查詢return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception e) {throw ExceptionFactory.wrapException('Error querying database. Cause: ' + e, e); } finally {ErrorContext.instance().reset(); }}

selectList有多個重載方法,進(jìn)入到最終方法后,我們可以看到它做了兩件事

通過statementId,從Configuration中取MappedStatement對象,就是存放了sql語句,返回值類型,輸入值類型的對象 然后委派Executor執(zhí)行器去執(zhí)行具體的增刪改查方法

所以,對于實際JDBC的操作,我們還需要進(jìn)入Executor中查看

Mybatis之Executor

我們繼續(xù)從剛剛selectList源碼中,進(jìn)入Executor查看

return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

//此方法在SimpleExecutor的父類BaseExecutor中實現(xiàn)@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { //根據(jù)傳入的參數(shù)動態(tài)獲得SQL語句,最后返回用BoundSql對象表示 BoundSql boundSql = ms.getBoundSql(parameter); //為本次查詢創(chuàng)建緩存的Key CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); // 查詢 return query(ms, parameter, rowBounds, resultHandler, key, boundSql);}

拆分成了三大步:

(1)先調(diào)用MappedStatement的getBoundSql方法,獲取解析后的SQL語句,解析工作是由SqlSourceBuilder完成的

什么叫解析后的SQL語句呢?因為Mybatis編寫SQL語句時,會用到動態(tài)SQL,比如#{}占位符,這種占位符JDBC是不認(rèn)識的,所以需要將其轉(zhuǎn)換成?占位符,并且將其內(nèi)部的字段名存儲起來,后面填充參數(shù)的時候好使用反射獲取值。

/** * 執(zhí)行解析原始 SQL ,成為 SqlSource 對象 * * @param originalSql 原始 SQL * @param parameterType 參數(shù)類型 * @param additionalParameters 附加參數(shù)集合。可能是空集合,也可能是 {@link org.apache.ibatis.scripting.xmltags.DynamicContext#bindings} 集合 * @return SqlSource 對象 */public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) { // 創(chuàng)建 ParameterMappingTokenHandler 對象 ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters); // 創(chuàng)建 GenericTokenParser 對象 GenericTokenParser parser = new GenericTokenParser('#{', '}', handler); // 執(zhí)行解析 String sql = parser.parse(originalSql); // 創(chuàng)建 StaticSqlSource 對象 return new StaticSqlSource(configuration, sql, handler.getParameterMappings());}

上面代碼就可以看到,會將拆分#{和},進(jìn)行解析

(2)根據(jù)查詢條件,創(chuàng)建緩存key,用來接下來去緩存查找是否有已經(jīng)執(zhí)行過的結(jié)果

(3)調(diào)用重載query()方法

接著我們進(jìn)入重載方法查看:

@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());// 已經(jīng)關(guān)閉,則拋出 ExecutorException 異常if (closed) { throw new ExecutorException('Executor was closed.');}// 清空本地緩存,如果 queryStack 為零,并且要求清空本地緩存。if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache();}List<E> list;try { // queryStack + 1 queryStack++; // 從一級緩存中,獲取查詢結(jié)果 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; // 獲取到,則進(jìn)行處理 if (list != null) {handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); // 獲得不到,則從數(shù)據(jù)庫中查詢 } else {list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); }} finally { // queryStack - 1 queryStack--;}if (queryStack == 0) { // 執(zhí)行延遲加載 for (DeferredLoad deferredLoad : deferredLoads) {deferredLoad.load(); } // issue #601 // 清空 deferredLoads deferredLoads.clear(); // 如果緩存級別是 LocalCacheScope.STATEMENT ,則進(jìn)行清理 if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {// issue #482clearLocalCache(); }}return list; }

主要的邏輯:

從一級緩存取數(shù)據(jù),如果有直接使用緩存的進(jìn)行接下來的操作 如果沒有,從數(shù)據(jù)庫查詢

進(jìn)入queryFromDatabase()方法:

// 從數(shù)據(jù)庫中讀取操作private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; // 在緩存中,添加占位對象。此處的占位符,和延遲加載有關(guān),可見 `DeferredLoad#canLoad()` 方法 localCache.putObject(key, EXECUTION_PLACEHOLDER); try {// 執(zhí)行讀操作list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally {// 從緩存中,移除占位對象localCache.removeObject(key); } // 添加到緩存中 localCache.putObject(key, list); // 暫時忽略,存儲過程相關(guān) if (ms.getStatementType() == StatementType.CALLABLE) {localOutputParameterCache.putObject(key, parameter); } return list;}@Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try {Configuration configuration = ms.getConfiguration();// 傳入?yún)?shù)創(chuàng)建StatementHanlder對象來執(zhí)行查詢StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);// 創(chuàng)建jdbc中的statement對象stmt = prepareStatement(handler, ms.getStatementLog());// 執(zhí)行 StatementHandler ,進(jìn)行讀操作return handler.query(stmt, resultHandler); } finally {// 關(guān)閉 StatementHandler 對象closeStatement(stmt); }}

通過代碼可以看到,對于實際與JDBC交互的代碼,Executor也懶得搞,又像SqlSession一樣,委派給小弟StatementHandler了。

Mybatis之StatementHandler

我們從剛剛的Executor的代碼查看

@Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try {Configuration configuration = ms.getConfiguration();// 傳入?yún)?shù)創(chuàng)建StatementHanlder對象來執(zhí)行查詢StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);// 創(chuàng)建jdbc中的statement對象stmt = prepareStatement(handler, ms.getStatementLog());// 執(zhí)行 StatementHandler ,進(jìn)行讀操作return handler.query(stmt, resultHandler); } finally {// 關(guān)閉 StatementHandler 對象closeStatement(stmt); }}

可以看到,這里創(chuàng)建完StatementHandler后,回調(diào)用prepareStatement()方法,用來創(chuàng)建Statement對象

我們進(jìn)入prepareStatement方法中查看

// 初始化 StatementHandler 對象private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; // 獲得 Connection 對象 Connection connection = getConnection(statementLog); // 創(chuàng)建 Statement 或 PrepareStatement 對象 stmt = handler.prepare(connection, transaction.getTimeout()); // 設(shè)置 SQL 上的參數(shù),例如 PrepareStatement 對象上的占位符 handler.parameterize(stmt); return stmt;}@Overridepublic void parameterize(Statement statement) throws SQLException { //使用ParameterHandler對象來完成對Statement的設(shè)值 parameterHandler.setParameters((PreparedStatement) statement);}

這里可以看到,它實際是使用ParameterHandler來設(shè)置Statement的參數(shù)

@Overridepublic void setParameters(PreparedStatement ps) { ErrorContext.instance().activity('setting parameters').object(mappedStatement.getParameterMap().getId()); // 遍歷 ParameterMapping 數(shù)組 List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) {for (int i = 0; i < parameterMappings.size(); i++) { // 獲得 ParameterMapping 對象 ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) {// 獲得值Object value;String propertyName = parameterMapping.getProperty();if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params value = boundSql.getAdditionalParameter(propertyName);} else if (parameterObject == null) { value = null;} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject;} else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName);}// 獲得 typeHandler、jdbcType 屬性TypeHandler typeHandler = parameterMapping.getTypeHandler();JdbcType jdbcType = parameterMapping.getJdbcType();if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull();}// 設(shè)置 ? 占位符的參數(shù)try { typeHandler.setParameter(ps, i + 1, value, jdbcType);} catch (TypeException | SQLException e) { throw new TypeException('Could not set parameters for mapping: ' + parameterMapping + '. Cause: ' + e, e);} }} }}

這段代碼的主要目的,就是獲取入?yún)ⅲ缓蟾鶕?jù)值,來設(shè)置?占位符的參數(shù)

TypeHandler是具體進(jìn)行參數(shù)設(shè)置的對象

所以handler.prepare(connection, transaction.getTimeout());方法,就是使用ParameterHandler來對占位符位置的參數(shù)進(jìn)行值設(shè)置

然后我們回到Executor,查看handler.query()方法

@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; // 執(zhí)行查詢 ps.execute(); // 處理返回結(jié)果 return resultSetHandler.handleResultSets(ps);}

代碼很簡單,這里直接使用JDBC的PreparedStatement來進(jìn)行SQL執(zhí)行,然后使用ResultSetHandler進(jìn)行結(jié)果數(shù)據(jù)封裝處理。

進(jìn)入ResultSetHandler

@Overridepublic List<Object> handleResultSets(Statement stmt) throws SQLException { ErrorContext.instance().activity('handling results').object(mappedStatement.getId()); // 多 ResultSet 的結(jié)果集合,每個 ResultSet 對應(yīng)一個 Object 對象。而實際上,每個 Object 是 List<Object> 對象。 // 在不考慮存儲過程的多 ResultSet 的情況,普通的查詢,實際就一個 ResultSet ,也就是說,multipleResults 最多就一個元素。 final List<Object> multipleResults = new ArrayList<>(); int resultSetCount = 0; // 獲得首個 ResultSet 對象,并封裝成 ResultSetWrapper 對象 ResultSetWrapper rsw = getFirstResultSet(stmt); // 獲得 ResultMap 數(shù)組 // 在不考慮存儲過程的多 ResultSet 的情況,普通的查詢,實際就一個 ResultSet ,也就是說,resultMaps 就一個元素。 List<ResultMap> resultMaps = mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); validateResultMapsCount(rsw, resultMapCount); // 校驗 while (rsw != null && resultMapCount > resultSetCount) {// 獲得 ResultMap 對象ResultMap resultMap = resultMaps.get(resultSetCount);// 處理 ResultSet ,將結(jié)果添加到 multipleResults 中handleResultSet(rsw, resultMap, multipleResults, null);// 獲得下一個 ResultSet 對象,并封裝成 ResultSetWrapper 對象rsw = getNextResultSet(stmt);// 清理cleanUpAfterHandlingResultSet();// resultSetCount ++resultSetCount++; } // 因為 `mappedStatement.resultSets` 只在存儲過程中使用,本系列暫時不考慮,忽略即可 // ··· // 如果是 multipleResults 單元素,則取首元素返回 return collapseSingleResultList(multipleResults);} // 處理 ResultSet ,將結(jié)果添加到 multipleResults 中private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException { try {// 暫時忽略,因為只有存儲過程的情況,調(diào)用該方法,parentMapping 為非空if (parentMapping != null) { handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);} else { // 如果沒有自定義的 resultHandler ,則創(chuàng)建默認(rèn)的 DefaultResultHandler 對象 if (resultHandler == null) {// 創(chuàng)建 DefaultResultHandler 對象DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);// 處理 ResultSet 返回的每一行 RowhandleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);// 添加 defaultResultHandler 的處理的結(jié)果,到 multipleResults 中multipleResults.add(defaultResultHandler.getResultList()); } else {// 處理 ResultSet 返回的每一行 RowhandleRowValues(rsw, resultMap, resultHandler, rowBounds, null); }} } finally {// issue #228 (close resultsets)// 關(guān)閉 ResultSet 對象closeResultSet(rsw.getResultSet()); }}

代碼比較多,實際最重要的代碼就是

// 添加 defaultResultHandler 的處理的結(jié)果,到 multipleResults 中multipleResults.add(defaultResultHandler.getResultList());

將處理后的結(jié)果封裝到集合中返回,這樣基本Mybatis邏輯就走完了.

我們來回顧一下,都用到了哪些類

淺談Mybatis SqlSession執(zhí)行流程

簡單總結(jié)SqlSessionFactoryBuilder:

解析核心配置文件,創(chuàng)建Configuration XMLConfigBuilder.parse():解析核心配置文件XMLMapperBuilder.parse():解析映射配置文件MappedStatement 創(chuàng)建SqlSessionFactory,默認(rèn)創(chuàng)建DefaultSqlSessionFactory

SqlSessionFactory:

openSession():構(gòu)建Executor,SqlSession等

SqlSession:

根據(jù)statementId獲取MappedStatement 委派給Executor執(zhí)行器執(zhí)行

Executor:

使用SqlSourceBuilder,將SQL解析成JDBC認(rèn)可的 查詢緩存,是否存在結(jié)果 結(jié)果不存在,委派給StatementHandler處理器

StatementHandler:

PreparedStatement:處理參數(shù),將參數(shù)賦值到占位符上 TypeHandler:具體設(shè)置值的類 ResultSetHandler:封裝結(jié)果集,封裝成設(shè)置的返回值類型 TypeHandler:根據(jù)結(jié)果集,取出對應(yīng)列

到此這篇關(guān)于淺談Mybatis SqlSession執(zhí)行流程的文章就介紹到這了,更多相關(guān)Mybatis SqlSession執(zhí)行流程內(nèi)容請搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Mybatis 數(shù)據(jù)庫
相關(guān)文章:
主站蜘蛛池模板: 免费视频18| 日本在线视频免费看 | 在线欧美一区 | 久久久久99精品成人片三人毛片 | 欧美日韩精品国产一区二区 | 国产高清精品毛片基地 | 亚洲网站视频在线观看 | 国产久草视频在线 | 国产一区二区三区影院 | 丁香婷婷影音先锋5566 | 久久草在线视频 | 奇米影视7777久久精品 | 女人18毛片a级毛片 女人aaaaa片一级一毛片 | 一级毛片视频在线 | 99re7在线精品免费视频 | 国产精品亚洲欧美日韩一区在线 | 99视频在线精品免费 | 中文乱码一二三四有限公司 | 91情国产l精品国产亚洲区 | 欧美亚洲国产成人精品 | 一级特黄aaa免费 | 久久久久久99精品 | 在线视频区 | 久久精品视频91 | 日韩精品在线观看免费 | 国产一区二区三区亚洲综合 | 性盈盈影院影院67194 | 国产第三区 | 久久久久久久久久毛片精品美女 | 国产欧美日韩精品高清二区综合区 | 小草青青神马影院 | 热re91久久精品国产91热 | 国产成人综合久久精品亚洲 | 国产玖玖在线观看 | 日韩午夜在线 | 亚洲欧美94色 | 亚洲午夜精品一级在线播放放 | 一级毛片中文字幕 | 日本不卡一二三区 | 日韩欧美中文字幕一区二区三区 | 国外成人在线视频 |