日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

MyBatis原理分析之四:一次SQL查询的源码分析

發布時間:2023/11/27 生活经验 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MyBatis原理分析之四:一次SQL查询的源码分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

上回我們講到Mybatis加載相關的配置文件進行初始化,這回我們講一下一次SQL查詢怎么進行的。

?

準備工作

Mybatis完成一次SQL查詢需要使用的代碼如下:

?

Java代碼??
  1. String?resource?=?"mybatis.cfg.xml";??
  2. ????????Reader?reader?=?Resources.getResourceAsReader(resource);??
  3. ????????SqlSessionFactory?ssf?=?new?SqlSessionFactoryBuilder().build(reader);??
  4. ??????????
  5. ??????<strong>??</strong>SqlSession?session?=?ssf.openSession();??
  6. ??????????
  7. ????????try?{??
  8. ????????????UserInfo?user?=?(UserInfo)?session.selectOne("User.selectUser",?"1");??
  9. ????????????System.out.println(user);??
  10. ????????}?catch?(Exception?e)?{??
  11. ????????????e.printStackTrace();??
  12. ????????}?finally?{??
  13. ????????????session.close();??
  14. ????????}??

?

本次我們需要進行深入跟蹤分析的是:

?

Java代碼??
  1. SqlSession?session?=?ssf.openSession();??
  2. ????????
  3. UserInfo?user?=?(UserInfo)?session.selectOne("User.selectUser",?"1");??
?

?

?

源碼分析

?

第一步:打開一個會話,我們看看里面具體做了什么事情。

?

Java代碼??
  1. SqlSession?session?=?ssf.openSession();??

?

?

DefaultSqlSessionFactory的?openSession()方法內容如下:

?

Java代碼??
  1. public?SqlSession?openSession()?{??
  2. ????return?openSessionFromDataSource(configuration.getDefaultExecutorType(),?null,?false);??
  3. ??}??
?

?

跟進去,我們看一下openSessionFromDataSource方法到底做了啥:

?

Java代碼??
  1. private?SqlSession?openSessionFromDataSource(ExecutorType?execType,?TransactionIsolationLevel?level,?boolean?autoCommit)?{??
  2. ????Connection?connection?=?null;??
  3. ????try?{??
  4. ??????final?Environment?environment?=?configuration.getEnvironment();??
  5. ??????final?DataSource?dataSource?=?getDataSourceFromEnvironment(environment);??
  6. ??????TransactionFactory?transactionFactory?=?getTransactionFactoryFromEnvironment(environment);??
  7. ??????connection?=?dataSource.getConnection();??
  8. ??????if?(level?!=?null)?{??
  9. ????????connection.setTransactionIsolation(level.getLevel());??
  10. ??????}??
  11. ??????connection?=?wrapConnection(connection);??
  12. ??????Transaction?tx?=?transactionFactory.newTransaction(connection,?autoCommit);??
  13. ??????Executor?executor?=?configuration.newExecutor(tx,?execType);??
  14. ??????return?new?DefaultSqlSession(configuration,?executor,?autoCommit);??
  15. ????}?catch?(Exception?e)?{??
  16. ??????closeConnection(connection);??
  17. ??????throw?ExceptionFactory.wrapException("Error?opening?session.??Cause:?"?+?e,?e);??
  18. ????}?finally?{??
  19. ??????ErrorContext.instance().reset();??
  20. ????}??
  21. ??}??
?

?

這里我們分析一下這里所涉及的步驟:

(1)獲取前面我們加載配置文件的環境信息,并且獲取環境信息中配置的數據源。

(2)通過數據源獲取一個連接,對連接進行包裝代理(通過JDK的代理來實現日志功能)。

(3)設置連接的事務信息(是否自動提交、事務級別),從配置環境中獲取事務工廠,事務工廠獲取一個新的事務。

(4)傳入事務對象獲取一個新的執行器,并傳入執行器、配置信息等獲取一個執行會話對象。

?

從上面的代碼我們可以得出,一次配置加載只能有且對應一個數據源。對于上述步驟,我們不難理解,我們重點看看新建執行器和DefaultSqlSession。

首先,我們看看newExecutor到底做了什么?

?

Java代碼??
  1. public?Executor?newExecutor(Transaction?transaction,?ExecutorType?executorType)?{??
  2. ???executorType?=?executorType?==?null???defaultExecutorType?:?executorType;??
  3. ???executorType?=?executorType?==?null???ExecutorType.SIMPLE?:?executorType;??
  4. ???Executor?executor;??
  5. ???if?(ExecutorType.BATCH?==?executorType)?{??
  6. ?????executor?=?new?BatchExecutor(this,?transaction);??
  7. ???}?else?if?(ExecutorType.REUSE?==?executorType)?{??
  8. ?????executor?=?new?ReuseExecutor(this,?transaction);??
  9. ???}?else?{??
  10. ?????executor?=?new?SimpleExecutor(this,?transaction);??
  11. ???}??
  12. ???if?(cacheEnabled)?{??
  13. ?????executor?=?new?CachingExecutor(executor);??
  14. ???}??
  15. ???executor?=?(Executor)?interceptorChain.pluginAll(executor);??
  16. ???return?executor;??
  17. ?}??

?

上面代碼的執行步驟如下:

(1)判斷執行器類型,如果配置文件中沒有配置執行器類型,則采用默認執行類型ExecutorType.SIMPLE。

(2)根據執行器類型返回不同類型的執行器(執行器有三種,分別是?BatchExecutor、SimpleExecutor和CachingExecutor,后面我們再詳細看看)。

(3)跟執行器綁定攔截器插件(這里也是使用代理來實現)。

?

DefaultSqlSession到底是干什么的呢?

DefaultSqlSession實現了SqlSession接口,里面有各種各樣的SQL執行方法,主要用于SQL操作的對外接口,它會的調用執行器來執行實際的SQL語句。

?

接下來我們看看SQL查詢是怎么進行的

?

?

Java代碼??
  1. UserInfo?user?=?(UserInfo)?session.selectOne("User.selectUser",?"1");??

?實際調用的是DefaultSqlSession類的selectOne方法,該方法代碼如下:

?

Java代碼??
  1. public?Object?selectOne(String?statement,?Object?parameter)?{??
  2. ????//?Popular?vote?was?to?return?null?on?0?results?and?throw?exception?on?too?many.??
  3. ????List?list?=?selectList(statement,?parameter);??
  4. ????if?(list.size()?==?1)?{??
  5. ??????return?list.get(0);??
  6. ????}?else?if?(list.size()?>?1)?{??
  7. ??????throw?new?TooManyResultsException("Expected?one?result?(or?null)?to?be?returned?by?selectOne(),?but?found:?"?+?list.size());??
  8. ????}?else?{??
  9. ??????return?null;??
  10. ????}??
  11. ??}??
?

我們再看看selectList方法(實際上是調用該類的另一個selectList方法來實現的):

?

Java代碼??
  1. public?List?selectList(String?statement,?Object?parameter)?{??
  2. ????return?selectList(statement,?parameter,?RowBounds.DEFAULT);??
  3. ??}??
  4. ??
  5. ??public?List?selectList(String?statement,?Object?parameter,?RowBounds?rowBounds)?{??
  6. ????try?{??
  7. ??????MappedStatement?ms?=?configuration.getMappedStatement(statement);??
  8. ??????return?executor.query(ms,?wrapCollection(parameter),?rowBounds,?Executor.NO_RESULT_HANDLER);??
  9. ????}?catch?(Exception?e)?{??
  10. ??????throw?ExceptionFactory.wrapException("Error?querying?database.??Cause:?"?+?e,?e);??
  11. ????}?finally?{??
  12. ??????ErrorContext.instance().reset();??
  13. ????}??
  14. ??}??

?

第二個selectList的執行步驟如下:

(1)根據SQL的ID到配置信息中找對應的MappedStatement,在之前配置被加載初始化的時候我們看到了系統會把配置文件中的SQL塊解析并放到一個MappedStatement里面,并將MappedStatement對象放到一個Map里面進行存放,Map的key值是該SQL塊的ID。

(2)調用執行器的query方法,傳入MappedStatement對象、SQL參數對象、范圍對象(此處為空)和結果處理方式。

?

好了,目前只剩下一個疑問,那就是執行器到底怎么執行SQL的呢?

?

上面我們知道了,默認情況下是采用SimpleExecutor執行的,我們看看這個類的doQuery方法:

?

Java代碼??
  1. public?List?doQuery(MappedStatement?ms,?Object?parameter,?RowBounds?rowBounds,?ResultHandler?resultHandler)?throws?SQLException?{??
  2. ????Statement?stmt?=?null;??
  3. ????try?{??
  4. ??????Configuration?configuration?=?ms.getConfiguration();??
  5. ??????StatementHandler?handler?=?configuration.newStatementHandler(this,?ms,?parameter,?rowBounds,?resultHandler);??
  6. ??????stmt?=?prepareStatement(handler);??
  7. ??????return?handler.query(stmt,?resultHandler);??
  8. ????}?finally?{??
  9. ??????closeStatement(stmt);??
  10. ????}??
  11. ??}??

?

doQuery方法的內部執行步驟:

(1) 獲取配置信息對象。

(2)通過配置對象獲取一個新的StatementHandler,該類主要用來處理一次SQL操作。

(3)預處理StatementHandler對象,得到Statement對象。

(4)傳入Statement和結果處理對象,通過StatementHandler的query方法來執行SQL,并對執行結果進行處理。

?

?

我們看一下newStatementHandler到底做了什么?

?

?

Java代碼??
  1. public?StatementHandler?newStatementHandler(Executor?executor,?MappedStatement?mappedStatement,?Object?parameterObject,?RowBounds?rowBounds,?ResultHandler?resultHandler)?{??
  2. ????StatementHandler?statementHandler?=?new?RoutingStatementHandler(executor,?mappedStatement,?parameterObject,?rowBounds,?resultHandler);??
  3. ????statementHandler?=?(StatementHandler)?interceptorChain.pluginAll(statementHandler);??
  4. ????return?statementHandler;??
  5. ??}??

?

?

上面代碼的執行步驟:

(1)根據相關的參數獲取對應的StatementHandler對象。

(2)為StatementHandler對象綁定攔截器插件。

?

RoutingStatementHandler類的構造方法RoutingStatementHandler如下:

?

Java代碼??
  1. public?RoutingStatementHandler(Executor?executor,?MappedStatement?ms,?Object?parameter,?RowBounds?rowBounds,?ResultHandler?resultHandler)?{??
  2. ??
  3. ???switch?(ms.getStatementType())?{??
  4. ?????case?STATEMENT:??
  5. ???????delegate?=?new?SimpleStatementHandler(executor,?ms,?parameter,?rowBounds,?resultHandler);??
  6. ???????break;??
  7. ?????case?PREPARED:??
  8. ???????delegate?=?new?PreparedStatementHandler(executor,?ms,?parameter,?rowBounds,?resultHandler);??
  9. ???????break;??
  10. ?????case?CALLABLE:??
  11. ???????delegate?=?new?CallableStatementHandler(executor,?ms,?parameter,?rowBounds,?resultHandler);??
  12. ???????break;??
  13. ?????default:??
  14. ???????throw?new?ExecutorException("Unknown?statement?type:?"?+?ms.getStatementType());??
  15. ???}??
  16. ??
  17. ?}??

?

根據?MappedStatement對象的StatementType來創建不同的StatementHandler,這個跟前面執行器的方式類似。StatementType有STATEMENT、PREPARED和CALLABLE三種類型,跟JDBC里面的Statement類型一一對應。

?

我們看一下prepareStatement方法具體內容:

Java代碼??
  1. private?Statement?prepareStatement(StatementHandler?handler)?throws?SQLException?{??
  2. ???Statement?stmt;??
  3. ???Connection?connection?=?transaction.getConnection();??
  4. ???//從連接中獲取Statement對象??
  5. ???stmt?=?handler.prepare(connection);??
  6. ???//處理預編譯的傳入參數??
  7. ???handler.parameterize(stmt);??
  8. ???return?stmt;??
  9. ?}??
?


來源:?http://chenjc-it.iteye.com/blog/1541947

null

轉載于:https://www.cnblogs.com/jeffen/p/6251197.html

總結

以上是生活随笔為你收集整理的MyBatis原理分析之四:一次SQL查询的源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。