mybatis源碼分析——執行過程分析

準備工作

我們首先導入mybatis源碼,首先以傳統方式執行一個查詢,通過打斷點來分析mybatis的整個查詢執行流程。

	@Test
    public void query() throws IOException {
        //1.Resources工具類,配置文件的加載,把配置文件加載成字節輸入流
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        //2.解析了配置文件,並創建了sqlSessionFactory工廠
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        //3.生產sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();// 默認開啓一個事務,但是該事務不會自動提交
                                                                //在進行增刪改操作時,要手動提交事務
        //4.sqlSession調用方法:查詢所有selectList  查詢單個:selectOne 添加:insert  修改:update 刪除:delete
        List<User> users = sqlSession.selectList("com.lagou.dao.IUserDao.findAll");

        for (User user : users) {
            System.out.println(user);
        }

        //IUserDao userDao = sqlSession.getMapper(IUserDao.class);
        //List<User> all = userDao.findAll();

        //System.out.println(all);

        sqlSession.close();

    }

執行過程:

1.讀取配置信息

public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
        InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
        if (in == null) {
            throw new IOException("Could not find resource " + resource);
        }
        return in;
    }

首先是讀取了配置文件,加載成輸入流將其返回

2.構建一個SqlSessionFactory

// 2.調用的重載方法
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            // 創建 XMLConfigBuilder, XMLConfigBuilder是專門解析mybatis的配置文件的類
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            // 執行 XML 解析
            // 創建 DefaultSqlSessionFactory 對象
            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.
            }
        }
    }

解析了配置文件,並創建了sqlSessionFactory工廠,在build方法中首先會解析創建出來一個Configuration對象出來,mybatis在初始化過程中,將配置信息全部加載到內存中,org.apache.ibatis.session.Configuration類來維護。Configuration的結構也與其xml的配置文件標籤基本相同。

 /**
     * 解析 XML 成 Configuration 對象。
     *
     * @return Configuration 對象
     */
    public Configuration parse() {
        // 若已解析,拋出 BuilderException 異常
        if (parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        }
        // 標記已解析
        parsed = true;
        ///parser是XPathParser解析器對象,讀取節點內數據,<configuration>是MyBatis配置文件中的頂層標籤
        // 解析 XML configuration 節點
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
    }
   /**
     * 解析 XML
     *
     * 具體 MyBatis 有哪些 XML 標籤,參見 《XML 映射配置文件》http://www.mybatis.org/mybatis-3/zh/configuration.html
     *
     * @param root 根節點
     */
    private void parseConfiguration(XNode root) {
        try {
            //issue #117 read properties first
            // 解析 <properties /> 標籤
            propertiesElement(root.evalNode("properties"));
            // 解析 <settings /> 標籤
            Properties settings = settingsAsProperties(root.evalNode("settings"));
            // 加載自定義的 VFS 實現類
            loadCustomVfs(settings);
            // 解析 <typeAliases /> 標籤
            typeAliasesElement(root.evalNode("typeAliases"));
            // 解析 <plugins /> 標籤
            pluginElement(root.evalNode("plugins"));
            // 解析 <objectFactory /> 標籤
            objectFactoryElement(root.evalNode("objectFactory"));
            // 解析 <objectWrapperFactory /> 標籤
            objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            // 解析 <reflectorFactory /> 標籤
            reflectorFactoryElement(root.evalNode("reflectorFactory"));
            // 賦值 <settings /> 到 Configuration 屬性
            settingsElement(settings);
            // read it after objectFactory and objectWrapperFactory issue #631
            // 解析 <environments /> 標籤
            environmentsElement(root.evalNode("environments"));
            // 解析 <databaseIdProvider /> 標籤
            databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            // 解析 <typeHandlers /> 標籤
            typeHandlerElement(root.evalNode("typeHandlers"));
            // 解析 <mappers /> 標籤
            mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
    }
/**
     * MappedStatement 映射
     *
     * KEY:`${namespace}.${id}`
     */
    protected final Map<String, MappedStatement> mappedStatements = new StrictMap<>("Mapped Statements collection");

在parse方法中,會完成對configuration的解析,需要注意的是在configuration對象中有着一個存儲類型爲map的mappedStatements,這個就是對應mapper當中的每一個select|insert|delete|update的節點,每一個節點都用MappedStatement這個對象所維護(其中維護了sqlText,resultType,parameterTyep等屬性),然後將這些節點對象都存在一個map當中。

/**
     * 創建 DefaultSqlSessionFactory 對象
     *
     * @param config Configuration 對象
     * @return DefaultSqlSessionFactory 對象
     */
    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config); //構建者設計模式
    }

然後完成一個sqlSessionFactory對象的包裝返回,這裏採用了構建者設計模式。

3.打開SqlSession

//6. 進入openSession方法
    @Override
    public SqlSession openSession() {
        //getDefaultExecutorType()傳遞的是SimpleExecutor
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
    }

openSession中有三個參數,

  • 第一個是一個執行器Executor,在configuration中默認爲simple的,
  • 第二個參數level爲數據庫事務的隔離級別,
  • 第三個參數autoCommit爲是否提交事務
//7. 進入openSessionFromDataSource。
    //ExecutorType 爲Executor的類型,TransactionIsolationLevel爲事務隔離級別,autoCommit是否開啓事務
    //openSession的多個重載方法可以指定獲得的SeqSession的Executor類型和事務的處理
    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();
        }
    }

獲取數據環境對象,事務對象,然後創建了一個executor執行器,最後返回一個sqlSession

4.執行sql獲取結果

//進入selectList方法,多個重載方法
    @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 {
            // 獲得 MappedStatement 對象
            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方法進行查詢,這裏的參數爲statementId和sql執行的參數,首先通過statement獲取到MappedStatement對象,這個對象就是在之前創建Configuration時中包含的對象,其中包含sql,返回結果類型,參數類型等相關信息。
然後通過executor執行器去執行獲取結果。

Executor執行器

BaseExecutor:

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

    @SuppressWarnings("unchecked")
    @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());
        // 已經關閉,則拋出 ExecutorException 異常
        if (closed) {
            throw new ExecutorException("Executor was closed.");
        }
        // 清空本地緩存,如果 queryStack 爲零,並且要求清空本地緩存。
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
            clearLocalCache();
        }
        List<E> list;
        try {
            // queryStack + 1
            queryStack++;
            // 從一級緩存中,獲取查詢結果
            list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
            // 獲取到,則進行處理
            if (list != null) {
                handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
            // 獲得不到,則從數據庫中查詢
            } else {
                list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
            }
        } finally {
            // queryStack - 1
            queryStack--;
        }
        if (queryStack == 0) {
            // 執行延遲加載
            for (DeferredLoad deferredLoad : deferredLoads) {
                deferredLoad.load();
            }
            // issue #601
            // 清空 deferredLoads
            deferredLoads.clear();
            // 如果緩存級別是 LocalCacheScope.STATEMENT ,則進行清理
            if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
                // issue #482
                clearLocalCache();
            }
        }
        return list;
    }
// 從數據庫中讀取操作
    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        List<E> list;
        // 在緩存中,添加佔位對象。此處的佔位符,和延遲加載有關,可見 `DeferredLoad#canLoad()` 方法
        localCache.putObject(key, EXECUTION_PLACEHOLDER);
        try {
            // 執行讀操作
            list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
            // 從緩存中,移除佔位對象
            localCache.removeObject(key);
        }
        // 添加到緩存中
        localCache.putObject(key, list);
        // 暫時忽略,存儲過程相關
        if (ms.getStatementType() == StatementType.CALLABLE) {
            localOutputParameterCache.putObject(key, parameter);
        }
        return list;
    }

在第一個query()中,首先獲取BoundSql ,這個類就是對sql進行一個封裝,然後創建一個緩存的key,最後執行第二個查詢方法query()
在第二個query()中,先用緩存key去從緩存中獲取結果對象,如果沒有則從數據庫查詢,執行queryFromDatabase()
queryFromDatabase(),去doQuery()獲取到查詢結果,然後存儲到緩存中。

SimpleExecutor:

@Override
    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;
        try {
            Configuration configuration = ms.getConfiguration();
            // 傳入參數創建StatementHanlder對象來執行查詢
            StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            // 創建jdbc中的statement對象
            stmt = prepareStatement(handler, ms.getStatementLog());
            // 執行 StatementHandler  ,進行讀操作
            return handler.query(stmt, resultHandler);
        } finally {
            // 關閉 StatementHandler 對象
            closeStatement(stmt);
        }
    }
    
	// 初始化 StatementHandler 對象
    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        // 獲得 Connection 對象
        Connection connection = getConnection(statementLog);
        // 創建 Statement 或 PrepareStatement 對象
        stmt = handler.prepare(connection, transaction.getTimeout());
        // 設置 SQL 上的參數,例如 PrepareStatement 對象上的佔位符
        handler.parameterize(stmt);
        return stmt;
    }

doQuery()中,會創建一個StatementHandler執行在查詢,prepareStatement()創建一個預編譯的對象返回。由handler執行查詢。到這一步執行器的任務就完成了,把查詢任務委派給了handler來處理。

StatementHandler

PreparedStatementHandler:

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

	@Override
    public void parameterize(Statement statement) throws SQLException {
        //使用ParameterHandler對象來完成對Statement的設值
        parameterHandler.setParameters((PreparedStatement) statement);
    }
    

DefaultParameterHandler:


	 @SuppressWarnings("Duplicates")
    @Override
    public void setParameters(PreparedStatement ps) {
        ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
        // 遍歷 ParameterMapping 數組
        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();
                    }
                    // 設置 ? 佔位符的參數
                    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);
                    }
                }
            }
        }
    }
    

DefaultResultSetHandler:

	//
    // HANDLE RESULT SETS
    //
    // 處理 {@link java.sql.ResultSet} 結果集
    @Override
    public List<Object> handleResultSets(Statement stmt) throws SQLException {
        ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

        // 多 ResultSet 的結果集合,每個 ResultSet 對應一個 Object 對象。而實際上,每個 Object 是 List<Object> 對象。
        // 在不考慮存儲過程的多 ResultSet 的情況,普通的查詢,實際就一個 ResultSet ,也就是說,multipleResults 最多就一個元素。
        final List<Object> multipleResults = new ArrayList<>();

        int resultSetCount = 0;
        // 獲得首個 ResultSet 對象,並封裝成 ResultSetWrapper 對象
        ResultSetWrapper rsw = getFirstResultSet(stmt);

        // 獲得 ResultMap 數組
        // 在不考慮存儲過程的多 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 ,將結果添加到 multipleResults 中
            handleResultSet(rsw, resultMap, multipleResults, null);
            // 獲得下一個 ResultSet 對象,並封裝成 ResultSetWrapper 對象
            rsw = getNextResultSet(stmt);
            // 清理
            cleanUpAfterHandlingResultSet();
            // resultSetCount ++
            resultSetCount++;
        }

        // 因爲 `mappedStatement.resultSets` 只在存儲過程中使用,本系列暫時不考慮,忽略即可
        String[] resultSets = mappedStatement.getResultSets();
        if (resultSets != null) {
            while (rsw != null && resultSetCount < resultSets.length) {
                ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
                if (parentMapping != null) {
                    String nestedResultMapId = parentMapping.getNestedResultMapId();
                    ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
                    handleResultSet(rsw, resultMap, null, parentMapping);
                }
                rsw = getNextResultSet(stmt);
                cleanUpAfterHandlingResultSet();
                resultSetCount++;
            }
        }

        // 如果是 multipleResults 單元素,則取首元素返回
        return collapseSingleResultList(multipleResults);
    }
// 處理 ResultSet ,將結果添加到 multipleResults 中
    private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
        try {
            // 暫時忽略,因爲只有存儲過程的情況,調用該方法,parentMapping 爲非空
            if (parentMapping != null) {
                handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
            } else {
                // 如果沒有自定義的 resultHandler ,則創建默認的 DefaultResultHandler 對象
                if (resultHandler == null) {
                    // 創建 DefaultResultHandler 對象
                    DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
                    // 處理 ResultSet 返回的每一行 Row
                    handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
                    // 添加 defaultResultHandler 的處理的結果,到 multipleResults 中
                    multipleResults.add(defaultResultHandler.getResultList());
                } else {
                    // 處理 ResultSet 返回的每一行 Row
                    handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
                }
            }
        } finally {
            // issue #228 (close resultsets)
            // 關閉 ResultSet 對象
            closeResultSet(rsw.getResultSet());
        }
    }
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
        // 創建 ResultLoaderMap 對象
        final ResultLoaderMap lazyLoader = new ResultLoaderMap();
        // 創建映射後的結果對象
        Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
        // 如果 hasTypeHandlerForResultObject(rsw, resultMap.getType()) 返回 true ,意味着 rowValue 是基本類型,無需執行下列邏輯。
        if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
            // 創建 MetaObject 對象,用於訪問 rowValue 對象
            final MetaObject metaObject = configuration.newMetaObject(rowValue);
            // foundValues 代表,是否成功映射任一屬性。若成功,則爲 true ,若失敗,則爲 false
            boolean foundValues = this.useConstructorMappings;
            /// 判斷是否開啓自動映射功能
            if (shouldApplyAutomaticMappings(resultMap, false)) {
                // 自動映射未明確的列
                foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
            }
            // 映射 ResultMap 中明確映射的列
            foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
            // ↑↑↑ 至此,當前 ResultSet 的該行記錄的數據,已經完全映射到結果對象 rowValue 的對應屬性種
            foundValues = lazyLoader.size() > 0 || foundValues;
            // 如果沒有成功映射任意屬性,則置空 rowValue 對象。
            // 當然,如果開啓 `configuration.returnInstanceForEmptyRow` 屬性,則不置空。默認情況下,該值爲 false
            rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
        }
        return rowValue;
    }

parameterHandler負責根據傳遞的參數值,對Statement對象設置參數,然後通過TypeHandler 完成jdbpType與javaType之間的轉換,對statement對象設置特定參數,
PreparedStatementHandler的query()方法對結果進行執行,交由resultSetHandler對返回結果進行處理,通過TypeHandler 對返回結果resultset取出特定的列,完成對結果封裝返回。
在這裏插入圖片描述
至此整個查詢結果就完成了。

發佈了24 篇原創文章 · 獲贊 11 · 訪問量 5527
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章