該系列文檔是本人在學習 Mybatis 的源碼過程當中總結下來的,可能對讀者不太友好,請結合個人源碼註釋(Mybatis源碼分析 GitHub 地址、Mybatis-Spring 源碼分析 GitHub 地址、Spring-Boot-Starter 源碼分析 GitHub 地址)進行閱讀html
MyBatis 版本:3.5.2java
MyBatis-Spring 版本:2.0.3git
MyBatis-Spring-Boot-Starter 版本:2.1.4github
在前面一系列的文檔中,已經詳細的介紹了 MyBatis 的初始化和執行 SQL 的過程,在執行 SQL 的過程當中,還存在着一些疑問,例如其中一直提到的 SqlSession 會話在 MyBatis 中是如何被建立的?如何調用 Executor 執行器執行 SQL 的?spring
那麼接下來就關於上面兩個的兩個問題,一塊兒來探討一下 MyBatis 中 SqlSession 會話的相關內容sql
先回顧一下《基礎支持層》的Binding模塊,每一個Mapper Interface
會有對應的MapperProxyFactory
動態代理對象工廠,用於建立MapperProxy
動態代理對象,Mapper 接口中的每一個方法都有對應的MapperMethod
對象,該對象會經過 SqlSession 會話執行數據操做數據庫
再來看到這張MyBatis總體圖:apache
在單獨使用 MyBatis 進行數據庫操做時,須要經過SqlSessionFactoryBuilder
構建一個SessionFactory
對象,而後經過它建立一個SqlSession
會話進行數據庫操做數組
咱們一般都會先調用SqlSession
會話的getMapper(Class<T> mapper)
方法,爲 Mapper 接口生成一個「實現類」(JDK動態代理對象),而後就能夠經過它進行數據庫操做緩存
// <1> 構建 SqlSessionFactory 對象 Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml"); // <2> 默認 DefaultSqlSessionFactory 對象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); // <3> 得到 SqlSession 對象,默認 DefaultSqlSession 對象 SqlSession sqlSession = sqlSessionFactory.openSession(); // <4> 得到 Mapper 對象 final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class); // <5> 執行查詢 final Object subject = mapper.getSubject(1);
org.apache.ibatis.session.SqlSessionFactoryBuilder
:構建SqlSessionFactory工廠類,裏面定義了許多build重載方法,主要分爲處理Reader和InputStream兩種文件資源對象,代碼以下:
public class SqlSessionFactoryBuilder { public SqlSessionFactory build(Reader reader) { return build(reader, null, null); } public SqlSessionFactory build(Reader reader, String environment) { return build(reader, environment, null); } public SqlSessionFactory build(Reader reader, Properties properties) { return build(reader, null, properties); } /** * 構造 SqlSessionFactory 對象 * * @param reader Reader 對象 * @param environment 環境 * @param properties Properties 變量 * @return SqlSessionFactory 對象 */ public SqlSessionFactory build(Reader reader, String environment, Properties properties) { try { /* * <1> 建立 XMLConfigBuilder 對象 * 會生成一個 XPathParser,包含 Document 對象 * 會建立一個 Configuration 全局配置對象 */ XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); /* * <2> 解析 XML 文件並配置到 Configuration 全局配置對象中 * <3> 建立 DefaultSqlSessionFactory 對象 */ return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } public SqlSessionFactory build(InputStream inputStream) { return build(inputStream, null, null); } public SqlSessionFactory build(InputStream inputStream, String environment) { return build(inputStream, environment, null); } public SqlSessionFactory build(InputStream inputStream, Properties properties) { return build(inputStream, null, properties); } public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); } }
在《MyBatis初始化(一)之加載mybatis-config.xml》中已經分析了,這裏就再也不贅述,就是根據文件資源建立Configuration全局配置對象,而後構建一個DefaultSqlSessionFactory對象
org.apache.ibatis.session.defaults.DefaultSqlSessionFactory
:實現 SqlSessionFactory 接口,默認的 SqlSessionFactory 實現類
openSession
方法,建立一個DefaultSqlSession
對象,以下:
public class DefaultSqlSessionFactory implements SqlSessionFactory { private final Configuration configuration; public DefaultSqlSessionFactory(Configuration configuration) { this.configuration = configuration; } @Override public SqlSession openSession() { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); } @Override public SqlSession openSession(boolean autoCommit) { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit); } @Override public SqlSession openSession(ExecutorType execType) { return openSessionFromDataSource(execType, null, false); } @Override public SqlSession openSession(TransactionIsolationLevel level) { return openSessionFromDataSource(configuration.getDefaultExecutorType(), level, false); } @Override public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) { return openSessionFromDataSource(execType, level, false); } @Override public SqlSession openSession(ExecutorType execType, boolean autoCommit) { return openSessionFromDataSource(execType, null, autoCommit); } @Override public SqlSession openSession(Connection connection) { return openSessionFromConnection(configuration.getDefaultExecutorType(), connection); } @Override public SqlSession openSession(ExecutorType execType, Connection connection) { return openSessionFromConnection(execType, connection); } }
openSession
有不少重載的方法,主要是提供如下幾種入參的支持:
類型 | 參數名稱 | 默認值 | 描述 |
---|---|---|---|
boolean | autoCommit | false | 事務是否自動提交 |
ExecutorType | execType | ExecutorType.SIMPLE | Executor執行器類型 |
TransactionIsolationLevel | level | 無 | 事務隔離級別 |
java.sql.Connection | connection | 無 | 數據庫鏈接 |
內部直接調用openSessionFromDataSource
私有方法,內部也須要調用openSessionFromConnection
私有方法,若是存在connection
入參,內部則直接調用openSessionFromConnection
私有方法
openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit)
方法,用於建立一個DefaultSqlSession
對象,方法以下:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { // 得到 Environment 對象 final Environment environment = configuration.getEnvironment(); // 建立 Transaction 對象 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); // 建立 Executor 對象 final Executor executor = configuration.newExecutor(tx, execType); // 建立 DefaultSqlSession 對象 return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { // 若是發生異常,則關閉 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(); } }
Environment
當前環境對象getTransactionFactoryFromEnvironment
方法,從 Environment 環境對象中TransactionFactory
對象,用於建立一個Transaction
事務Executor
執行器對象DefaultSqlSession
對象openSessionFromConnection(ExecutorType execType, Connection connection)
方法,用於建立一個DefaultSqlSession
對象
和上面的方法邏輯相同,只不過它的入參直接傳入了一個Connection數據庫鏈接,方法以下:
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) { try { // 得到是否能夠自動提交 boolean autoCommit; try { autoCommit = connection.getAutoCommit(); } catch (SQLException e) { // Failover to true, as most poor drivers // or databases won't support transactions autoCommit = true; } // 得到 Environment 對象 final Environment environment = configuration.getEnvironment(); // 建立 Transaction 對象 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); final Transaction tx = transactionFactory.newTransaction(connection); // 建立 Executor 對象 final Executor executor = configuration.newExecutor(tx, execType); // 建立 DefaultSqlSession 對象 return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
和上面的方法不一樣的是,建立Transaction
事務對象時,傳入的參數直接是Connection
數據庫鏈接對象
getTransactionFactoryFromEnvironment(Environment environment)
方法,用於建立一個TransactionFactory
對象,在建立 SqlSessionFactory 時,就能夠經過設置 Environment 的 DataSource 數據源和 TransactionFactory 事務工廠來集成第三方數據源和事務管理器,代碼以下:
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) { // 狀況一,建立 ManagedTransactionFactory 對象 if (environment == null || environment.getTransactionFactory() == null) { return new ManagedTransactionFactory(); } // 狀況二,使用 `environment` 中的 return environment.getTransactionFactory(); }
closeTransaction(Transaction tx)
方法,關閉事務,方法以下:
private void closeTransaction(Transaction tx) { if (tx != null) { try { tx.close(); } catch (SQLException ignore) { // Intentionally ignore. Prefer previous error. } } }
org.apache.ibatis.session.defaults.DefaultSqlSession
:實現 SqlSession 接口,默認的 SqlSession 實現類,調用 Executor 執行器,執行數據庫操做
public class DefaultSqlSession implements SqlSession { /** * 全局配置 */ private final Configuration configuration; /** * 執行器對象 */ private final Executor executor; /** * 是否自動提交事務 */ private final boolean autoCommit; /** * 是否發生數據變動 */ private boolean dirty; /** * Cursor 數組 */ private List<Cursor<?>> cursorList; public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) { this.configuration = configuration; this.executor = executor; this.dirty = false; this.autoCommit = autoCommit; } }
執行數據庫查詢操做,提供了許多重載方法
@Override public void select(String statement, Object parameter, ResultHandler handler) { select(statement, parameter, RowBounds.DEFAULT, handler); } @Override public void select(String statement, ResultHandler handler) { select(statement, null, RowBounds.DEFAULT, handler); } @Override 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(); } } @Override public <T> T selectOne(String statement) { return this.selectOne(statement, null); } @Override public <T> T selectOne(String statement, Object parameter) { // Popular vote was to return null on 0 results and throw exception on too many. List<T> list = this.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; } } @Override public <K, V> Map<K, V> selectMap(String statement, String mapKey) { return this.selectMap(statement, null, mapKey, RowBounds.DEFAULT); } @Override public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) { return this.selectMap(statement, parameter, mapKey, RowBounds.DEFAULT); } @Override public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) { // <1> 執行查詢 final List<? extends V> list = selectList(statement, parameter, rowBounds); // <2> 建立 DefaultMapResultHandler 對象 final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<>(mapKey, configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory()); // <3> 建立 DefaultResultContext 對象 final DefaultResultContext<V> context = new DefaultResultContext<>(); // <4> 遍歷查詢結果 for (V o : list) { // 設置 DefaultResultContext 中 context.nextResultObject(o); // 使用 DefaultMapResultHandler 處理結果的當前元素 mapResultHandler.handleResult(context); } // <5> 返回結果 return mapResultHandler.getMappedResults(); } @Override public <E> List<E> selectList(String statement) { return this.selectList(statement, null); } @Override public <E> List<E> selectList(String statement, Object parameter) { return this.selectList(statement, parameter, RowBounds.DEFAULT); } @Override public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { // <1> 得到 MappedStatement 對象 MappedStatement ms = configuration.getMappedStatement(statement); // <2> 執行查詢 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(); } }
上面有不少的數據庫查詢方法,主要分爲如下幾種:
select
:執行數據庫查詢操做,經過入參中的ResultHandler
處理結果集,無返回結果selectList
:執行數據庫查詢操做,返回List集合selectOne
:調用selectList
方法,執行數據庫查詢操做,最多隻能返回一條數據selectMap
:調用selectList
方法,執行數據庫查詢操做,經過DefaultMapResultHandler
進行處理,將返回結果轉換成Map集合能夠看到經過Executor執行器的query
方法執行查詢操做,能夠回顧《SQL執行過程(一)之Executor》中的內容
這裏會先調用wrapCollection
方法對入參進行包裝(若是是集合類型)
執行數據庫更新操做
@Override public int insert(String statement) { return insert(statement, null); } @Override public int insert(String statement, Object parameter) { return update(statement, parameter); } @Override public int delete(String statement) { return update(statement, null); } @Override public int delete(String statement, Object parameter) { return update(statement, parameter); } @Override public int update(String statement) { return update(statement, null); } @Override public int update(String statement, Object parameter) { try { // <1> 標記 dirty ,表示執行過寫操做 dirty = true; // <2> 得到 MappedStatement 對象 MappedStatement ms = configuration.getMappedStatement(statement); // <3> 執行更新操做 return executor.update(ms, wrapCollection(parameter)); } catch (Exception e) { throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
insert
和delete
方法最終都是調用update
方法,經過Executor執行器的update
方法執行數據庫更新操做
這裏會先調用wrapCollection
方法對入參進行包裝(若是是集合類型)
wrapCollection(final Object object)
方法,將集合類型的參數包裝成StrictMap
對象,方法以下:
public static class StrictMap<V> extends HashMap<String, V> { private static final long serialVersionUID = -5741767162221585340L; @Override public V get(Object key) { if (!super.containsKey(key)) { throw new BindingException( "Parameter '" + key + "' not found. Available parameters are " + this.keySet()); } return super.get(key); } } private Object wrapCollection(final Object object) { if (object instanceof Collection) { // 若是是集合,則添加到 collection 中 StrictMap<Object> map = new StrictMap<>(); map.put("collection", object); // 若是是 List ,則添加到 list 中 if (object instanceof List) { map.put("list", object); } return map; } else if (object != null && object.getClass().isArray()) { // 若是是 Array ,則添加到 array 中 StrictMap<Object> map = new StrictMap<>(); map.put("array", object); return map; } return object; }
getMapper(Class<T> type)
方法,獲取Mapper接口的代理對象
@Override public <T> T getMapper(Class<T> type) { return configuration.getMapper(type, this); }
經過Configuration
全局配置對象獲取一個動態代理對象
實際經過MapperRegistry
註冊表獲取到該Mapper接口對應的MapperProxyFactory
動態代理工廠,而後建立一個MapperProxy
動態代理的實例對象
flushStatements()
:提交批處理commit()
:提交事務rollback()
:回滾事務close()
:關閉當前會話getConnection()
:獲取當前事務的數據庫鏈接clearCache()
:清理一級緩存org.apache.ibatis.binding.MapperMethod
:Mapper接口中定義的方法對應的Mapper方法,經過它來執行SQL
先來看看執行一個SQL的完整流程圖:
在《基礎支持層》的Binding模塊已經講過了相關的內容,例如看到前面示例的第4
步,經過DefaultSqlSession
的getMapper
方法會執行如下操做:
MapperProxyFactory
動態代理對象工廠newInstance
方法會建立一個MapperProxy
接口代理類,而後返回該Mapper接口的動態代理對象當你調用這個接口的某個方法時,會進入這個MapperProxy
代理類,咱們來看到它的invoke
方法是如何實現的,方法以下:
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { // <1> 若是是 Object 定義的方法,直接調用 if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else if (method.isDefault()) { // 是否有 default 修飾的方法 // 針對Java7以上版本對動態類型語言的支持 if (privateLookupInMethod == null) { return invokeDefaultMethodJava8(proxy, method, args); } else { return invokeDefaultMethodJava9(proxy, method, args); } } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } // <2.1> 得到 MapperMethod 對象 final MapperMethod mapperMethod = cachedMapperMethod(method); // <2.2> 執行 MapperMethod 方法 return mapperMethod.execute(sqlSession, args); }
首先獲取到方法對應的MapperMethod
對象,而後經過該對象去執行數據庫的操做
public Object execute(SqlSession sqlSession, Object[] args) { // 根據 SqlCommand 的 Type 判斷應該如何執行 SQL 語句 Object result; switch (command.getType()) { case INSERT: { // <1> 獲取參數值與參數名的映射 Object param = method.convertArgsToSqlCommandParam(args); // <2> 而後經過 SqlSession 進行數據庫更新操做,並將受影響行數轉換爲結果 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()) { // 無返回,且入參中有 ResultHandler 結果處理器 executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { // 執行查詢,返回列表 result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { // 執行查詢,返回 Map result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) { // 執行查詢,返回 Cursor result = executeForCursor(sqlSession, args); } else { // 執行查詢,返回單個對象 // 獲取參數名稱與入參的映射 Object param = method.convertArgsToSqlCommandParam(args); // 執行查詢,返回單條數據 result = sqlSession.selectOne(command.getName(), param); if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) { result = Optional.ofNullable(result); } } break; case FLUSH: result = sqlSession.flushStatements(); break; default: throw new BindingException("Unknown execution method for: " + command.getName()); } // 返回結果爲 null ,而且返回類型爲原始類型(基本類型),則拋出 BindingException 異常 if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); } return result; }
根據 SqlCommand 的 Type 判斷應該如何執行 SQL 語句,類型分爲INSERT
、UPDATE
、DELETE
、SELECT
和FLUSH
(進行批處理)五種
前三種都屬於數據庫的更新操做,調用的是DefaultSqlSession
的update
方法,經過rowCountResult(int rowCount)
將受影響行數轉換爲返回對象
查詢語句的話就分爲如下幾種狀況:
ResultHandler
結果處理器,則調用executeWithResultHandler
方法,執行查詢,返回結果設置爲nullexecuteForMany
方法,執行查詢,返回列表executeForMap
方法,執行查詢,返回MapexecuteForCursor
方法,執行查詢,返回CursorDefaultSqlSession
的selectOne
方法,執行查詢,返回單個對象上面執行數據庫操做前會先調用convertArgsToSqlCommandParam
方法,獲取參數值與參數名的映射
convertArgsToSqlCommandParam(Object[] args)
方法,根據入參返回參數名稱與入參的映射,方法以下:
public Object convertArgsToSqlCommandParam(Object[] args) { return paramNameResolver.getNamedParams(args); }
在《基礎支持層》的反射模塊的ParamNameResolver小節已經分析過該方法,能夠跳過去看看
rowCountResult(int rowCount)
方法,將受影響行數轉換爲結果,方法以下:
private Object rowCountResult(int rowCount) { final Object result; if (method.returnsVoid()) { result = null; } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) { result = rowCount; } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) { result = (long) rowCount; } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) { result = rowCount > 0; } else { throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType()); } return result; }
executeWithResultHandler(SqlSession sqlSession, Object[] args)
方法,經過入參中定義的ResultHandler結果處理器執行查詢,方法以下:
private void executeWithResultHandler(SqlSession sqlSession, Object[] args) { // <1> 得到 MappedStatement 對象 MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName()); /* * <2> 配置校驗 * 由於入參定義了 ResultHandler 結果處理器,因此若是不是存儲過程,且沒有配置返回結果的 Java Type,則會拋出異常 */ if (!StatementType.CALLABLE.equals(ms.getStatementType()) && void.class.equals(ms.getResultMaps().get(0).getType())) { throw new BindingException( "method " + command.getName() + " needs either a @ResultMap annotation, a @ResultType annotation," + " or a resultType attribute in XML so a ResultHandler can be used as a parameter."); } // <3> 獲取參數名稱與入參的映射 Object param = method.convertArgsToSqlCommandParam(args); // <4> 執行數據庫查詢操做 if (method.hasRowBounds()) { // <4.1> 入參定義了 RowBounds 分頁對象 // <4.1.1> 獲取入參定義了 RowBounds 分頁對象 RowBounds rowBounds = method.extractRowBounds(args); // <4.1.2> 執行查詢 sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args)); } else { // <4.2> 執行查詢 sqlSession.select(command.getName(), param, method.extractResultHandler(args)); } }
MappedStatement
對象ResultHandler
結果處理器,因此若是不是存儲過程,且沒有配置返回結果的 Java Type,則會拋出異常RowBounds
分頁對象,則獲取該對象,而後執行查詢,傳入分析對象executeForMany(SqlSession sqlSession, Object[] args)
方法,執行查詢,返回列表,方法以下:
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) { List<E> result; // 獲取參數名稱與入參的映射 Object param = method.convertArgsToSqlCommandParam(args); // 執行數據庫查詢操做 if (method.hasRowBounds()) { RowBounds rowBounds = method.extractRowBounds(args); // 執行查詢,返回 List 集合 result = sqlSession.selectList(command.getName(), param, rowBounds); } else { // 執行查詢,返回 List 集合 result = sqlSession.selectList(command.getName(), param); } // issue #510 Collections & arrays support // 封裝 Array 或 Collection 結果 if (!method.getReturnType().isAssignableFrom(result.getClass())) { // 若是不是 List 集合類型 if (method.getReturnType().isArray()) { // 將 List 轉換成 Array 數組類型的結果 return convertToArray(result); } else { // 轉換成其餘 Collection 集合類型的結果 return convertToDeclaredCollection(sqlSession.getConfiguration(), result); } } // 直接返回 List 集合類型的結果 return result; }
executeForMap(SqlSession sqlSession, Object[] args)
方法,執行查詢,返回Map,方法以下:
private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) { Map<K, V> result; // 獲取參數名稱與入參的映射 Object param = method.convertArgsToSqlCommandParam(args); // 執行 SELECT 操做 if (method.hasRowBounds()) { RowBounds rowBounds = method.extractRowBounds(args); // 執行查詢,返回 Map 集合 result = sqlSession.selectMap(command.getName(), param, method.getMapKey(), rowBounds); } else { // 執行查詢,返回 Map 集合 result = sqlSession.selectMap(command.getName(), param, method.getMapKey()); } return result; }
本分分析了 SqlSession 會話在 MyBatis 中是如何被建立,如何獲取到 Mapper 接口的動態代理對象,經過該動態代理對象是如何執行 SQL 的
SqlSessionFactoryBuilder
構造器提供build
方法,根據mybatis-config.xml
配置文件對 MyBatis 進行初始化,生成Configuration
對象,用於構建一個DefaultSqlSessionFactory
對象
經過1
生成的 SqlSession 工廠對象,咱們能夠構建一個DefaultSqlSession
會話對象,經過這個會話對象的getMapper(Class<T> mapper)
方法,能夠爲 Mapper 接口建立一個動態代理對象
(JDK動態代理),其動態代理類爲MapperProxy
對象
調用 Mapper 接口的某個方法時,會進入相應的MapperProxy
代理類,根據方法對應的MapperMethod
對象,執行數據庫操做
執行數據庫相關操做,調用的就是上面的DefaultSqlSession
會話對象的相關方法,該會話內部則會經過Executor
執行器去執行數據庫的相關操做,並返回執行結果
好了,對於 MyBatis 總體上全部的內容已經所有分析完了,相信你們對 MyBatis 有了一個全面認識,其中確定有不對或者迷惑的地方,歡迎指正!!!感謝你們的閱讀!!!😄😄😄
參考文章:芋道源碼《精盡 MyBatis 源碼分析》