MyBatis原理分析之四:一次SQL查询的源码分析
上回我們講到Mybatis加載相關的配置文件進行初始化,這回我們講一下一次SQL查詢怎么進行的。
?
準備工作
Mybatis完成一次SQL查詢需要使用的代碼如下:
?
Java代碼??- String?resource?=?"mybatis.cfg.xml";??
- ????????Reader?reader?=?Resources.getResourceAsReader(resource);??
- ????????SqlSessionFactory?ssf?=?new?SqlSessionFactoryBuilder().build(reader);??
- ??????????
- ??????<strong>??</strong>SqlSession?session?=?ssf.openSession();??
- ??????????
- ????????try?{??
- ????????????UserInfo?user?=?(UserInfo)?session.selectOne("User.selectUser",?"1");??
- ????????????System.out.println(user);??
- ????????}?catch?(Exception?e)?{??
- ????????????e.printStackTrace();??
- ????????}?finally?{??
- ????????????session.close();??
- ????????}??
?
本次我們需要進行深入跟蹤分析的是:
?
Java代碼??- SqlSession?session?=?ssf.openSession();??
- ????????
- UserInfo?user?=?(UserInfo)?session.selectOne("User.selectUser",?"1");??
?
?
源碼分析
?
第一步:打開一個會話,我們看看里面具體做了什么事情。
?
Java代碼??- SqlSession?session?=?ssf.openSession();??
?
?
DefaultSqlSessionFactory的?openSession()方法內容如下:
?
Java代碼??- public?SqlSession?openSession()?{??
- ????return?openSessionFromDataSource(configuration.getDefaultExecutorType(),?null,?false);??
- ??}??
?
跟進去,我們看一下openSessionFromDataSource方法到底做了啥:
?
Java代碼??- private?SqlSession?openSessionFromDataSource(ExecutorType?execType,?TransactionIsolationLevel?level,?boolean?autoCommit)?{??
- ????Connection?connection?=?null;??
- ????try?{??
- ??????final?Environment?environment?=?configuration.getEnvironment();??
- ??????final?DataSource?dataSource?=?getDataSourceFromEnvironment(environment);??
- ??????TransactionFactory?transactionFactory?=?getTransactionFactoryFromEnvironment(environment);??
- ??????connection?=?dataSource.getConnection();??
- ??????if?(level?!=?null)?{??
- ????????connection.setTransactionIsolation(level.getLevel());??
- ??????}??
- ??????connection?=?wrapConnection(connection);??
- ??????Transaction?tx?=?transactionFactory.newTransaction(connection,?autoCommit);??
- ??????Executor?executor?=?configuration.newExecutor(tx,?execType);??
- ??????return?new?DefaultSqlSession(configuration,?executor,?autoCommit);??
- ????}?catch?(Exception?e)?{??
- ??????closeConnection(connection);??
- ??????throw?ExceptionFactory.wrapException("Error?opening?session.??Cause:?"?+?e,?e);??
- ????}?finally?{??
- ??????ErrorContext.instance().reset();??
- ????}??
- ??}??
?
這里我們分析一下這里所涉及的步驟:
(1)獲取前面我們加載配置文件的環境信息,并且獲取環境信息中配置的數據源。
(2)通過數據源獲取一個連接,對連接進行包裝代理(通過JDK的代理來實現日志功能)。
(3)設置連接的事務信息(是否自動提交、事務級別),從配置環境中獲取事務工廠,事務工廠獲取一個新的事務。
(4)傳入事務對象獲取一個新的執行器,并傳入執行器、配置信息等獲取一個執行會話對象。
?
從上面的代碼我們可以得出,一次配置加載只能有且對應一個數據源。對于上述步驟,我們不難理解,我們重點看看新建執行器和DefaultSqlSession。
首先,我們看看newExecutor到底做了什么?
?
Java代碼??- public?Executor?newExecutor(Transaction?transaction,?ExecutorType?executorType)?{??
- ???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);??
- ???}??
- ???if?(cacheEnabled)?{??
- ?????executor?=?new?CachingExecutor(executor);??
- ???}??
- ???executor?=?(Executor)?interceptorChain.pluginAll(executor);??
- ???return?executor;??
- ?}??
?
上面代碼的執行步驟如下:
(1)判斷執行器類型,如果配置文件中沒有配置執行器類型,則采用默認執行類型ExecutorType.SIMPLE。
(2)根據執行器類型返回不同類型的執行器(執行器有三種,分別是?BatchExecutor、SimpleExecutor和CachingExecutor,后面我們再詳細看看)。
(3)跟執行器綁定攔截器插件(這里也是使用代理來實現)。
?
DefaultSqlSession到底是干什么的呢?
DefaultSqlSession實現了SqlSession接口,里面有各種各樣的SQL執行方法,主要用于SQL操作的對外接口,它會的調用執行器來執行實際的SQL語句。
?
接下來我們看看SQL查詢是怎么進行的
?
?
Java代碼??- UserInfo?user?=?(UserInfo)?session.selectOne("User.selectUser",?"1");??
?實際調用的是DefaultSqlSession類的selectOne方法,該方法代碼如下:
?
Java代碼??- public?Object?selectOne(String?statement,?Object?parameter)?{??
- ????//?Popular?vote?was?to?return?null?on?0?results?and?throw?exception?on?too?many.??
- ????List?list?=?selectList(statement,?parameter);??
- ????if?(list.size()?==?1)?{??
- ??????return?list.get(0);??
- ????}?else?if?(list.size()?>?1)?{??
- ??????throw?new?TooManyResultsException("Expected?one?result?(or?null)?to?be?returned?by?selectOne(),?but?found:?"?+?list.size());??
- ????}?else?{??
- ??????return?null;??
- ????}??
- ??}??
我們再看看selectList方法(實際上是調用該類的另一個selectList方法來實現的):
?
Java代碼??- public?List?selectList(String?statement,?Object?parameter)?{??
- ????return?selectList(statement,?parameter,?RowBounds.DEFAULT);??
- ??}??
- ??
- ??public?List?selectList(String?statement,?Object?parameter,?RowBounds?rowBounds)?{??
- ????try?{??
- ??????MappedStatement?ms?=?configuration.getMappedStatement(statement);??
- ??????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的執行步驟如下:
(1)根據SQL的ID到配置信息中找對應的MappedStatement,在之前配置被加載初始化的時候我們看到了系統會把配置文件中的SQL塊解析并放到一個MappedStatement里面,并將MappedStatement對象放到一個Map里面進行存放,Map的key值是該SQL塊的ID。
(2)調用執行器的query方法,傳入MappedStatement對象、SQL參數對象、范圍對象(此處為空)和結果處理方式。
?
好了,目前只剩下一個疑問,那就是執行器到底怎么執行SQL的呢?
?
上面我們知道了,默認情況下是采用SimpleExecutor執行的,我們看看這個類的doQuery方法:
?
Java代碼??- public?List?doQuery(MappedStatement?ms,?Object?parameter,?RowBounds?rowBounds,?ResultHandler?resultHandler)?throws?SQLException?{??
- ????Statement?stmt?=?null;??
- ????try?{??
- ??????Configuration?configuration?=?ms.getConfiguration();??
- ??????StatementHandler?handler?=?configuration.newStatementHandler(this,?ms,?parameter,?rowBounds,?resultHandler);??
- ??????stmt?=?prepareStatement(handler);??
- ??????return?handler.query(stmt,?resultHandler);??
- ????}?finally?{??
- ??????closeStatement(stmt);??
- ????}??
- ??}??
?
doQuery方法的內部執行步驟:
(1) 獲取配置信息對象。
(2)通過配置對象獲取一個新的StatementHandler,該類主要用來處理一次SQL操作。
(3)預處理StatementHandler對象,得到Statement對象。
(4)傳入Statement和結果處理對象,通過StatementHandler的query方法來執行SQL,并對執行結果進行處理。
?
?
我們看一下newStatementHandler到底做了什么?
?
?
Java代碼??- public?StatementHandler?newStatementHandler(Executor?executor,?MappedStatement?mappedStatement,?Object?parameterObject,?RowBounds?rowBounds,?ResultHandler?resultHandler)?{??
- ????StatementHandler?statementHandler?=?new?RoutingStatementHandler(executor,?mappedStatement,?parameterObject,?rowBounds,?resultHandler);??
- ????statementHandler?=?(StatementHandler)?interceptorChain.pluginAll(statementHandler);??
- ????return?statementHandler;??
- ??}??
?
?
上面代碼的執行步驟:
(1)根據相關的參數獲取對應的StatementHandler對象。
(2)為StatementHandler對象綁定攔截器插件。
?
RoutingStatementHandler類的構造方法RoutingStatementHandler如下:
?
Java代碼??- public?RoutingStatementHandler(Executor?executor,?MappedStatement?ms,?Object?parameter,?RowBounds?rowBounds,?ResultHandler?resultHandler)?{??
- ??
- ???switch?(ms.getStatementType())?{??
- ?????case?STATEMENT:??
- ???????delegate?=?new?SimpleStatementHandler(executor,?ms,?parameter,?rowBounds,?resultHandler);??
- ???????break;??
- ?????case?PREPARED:??
- ???????delegate?=?new?PreparedStatementHandler(executor,?ms,?parameter,?rowBounds,?resultHandler);??
- ???????break;??
- ?????case?CALLABLE:??
- ???????delegate?=?new?CallableStatementHandler(executor,?ms,?parameter,?rowBounds,?resultHandler);??
- ???????break;??
- ?????default:??
- ???????throw?new?ExecutorException("Unknown?statement?type:?"?+?ms.getStatementType());??
- ???}??
- ??
- ?}??
?
根據?MappedStatement對象的StatementType來創建不同的StatementHandler,這個跟前面執行器的方式類似。StatementType有STATEMENT、PREPARED和CALLABLE三種類型,跟JDBC里面的Statement類型一一對應。
?
我們看一下prepareStatement方法具體內容:
Java代碼??- private?Statement?prepareStatement(StatementHandler?handler)?throws?SQLException?{??
- ???Statement?stmt;??
- ???Connection?connection?=?transaction.getConnection();??
- ???//從連接中獲取Statement對象??
- ???stmt?=?handler.prepare(connection);??
- ???//處理預編譯的傳入參數??
- ???handler.parameterize(stmt);??
- ???return?stmt;??
- ?}??
null
轉載于:https://www.cnblogs.com/jeffen/p/6251197.html
總結
以上是生活随笔為你收集整理的MyBatis原理分析之四:一次SQL查询的源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一般的红木家具大概多少钱一套啊?
- 下一篇: ueeditor 百度编译器使用onch