Mybatis執行SQL的流程


學習自拉鈎教育java高薪訓練營

前篇:Mybatis初始化過程

SqlSession

SqlSession是一個接口,它有兩個實現類:DefaultSqlSession (默認)和

SqlSessionManager (棄用,不做介紹)

SqlSession是MyBatis中用於和數據庫交互的頂層類,通常將它與ThreadLocal綁定,一個會話使用一 個

SqlSession,並且在使用完畢后需要close (SqlSession是線程不安全的,因此我們使用完后要記得close)

public class DefaultSqlSession implements SqlSession {
    private final Configuration configuration; 
    private final Executor executor;
    //SqlSession中的兩個最重要的參數,configuration與初始化時的相同,Executor為執行器
	//......
}

Executor

Executor也是一個接口,他有三個常用的實現類:

BatchExecutor (重用語句並執行批量更新)

ReuseExecutor (重用預處理語句 prepared statements)

SimpleExecutor (普通的執行器,默認)

源碼分析:

1. SqlSession sqlSession = sqlSessionFactory.openSession();

// 1. 讀取配置文件,讀成字節輸入流,注意:現在還沒解析
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
// 2. 解析配置文件,封裝Configuration對象   創建DefaultSqlSessionFactory對象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

// 3. 生產了DefaultSqlsession實例對象   設置了事務不自動提交  完成了executor對象的創建
SqlSession sqlSession = sqlSessionFactory.openSession();
//進入openSession方法
@Override
public SqlSession openSession() {
    //getDefaultExecutorType()傳遞的是SimpleExecutor
    //第一個參數:當前默認執行器的類型
    //第二個參數:數據庫的隔離級別
    //第三個參數:是否自動提交事務
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

//configuration.getDefaultExecutorType() --- 當前默認執行器的類型
public ExecutorType getDefaultExecutorType() {
    return defaultExecutorType;
}

protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
//進入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();
    }
}

這一步我們就完成了SqlSession對象的創建

2. List objects = sqlSession.selectList("namespace.id");
// 4.(1)根據statementid來從Configuration中map集合中獲取到了指定的MappedStatement對象
   //(2)將查詢任務委派了executor執行器
List<Object> objects = sqlSession.selectList("namespace.id");
//DefaultSqlSession.java
@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 statementId, 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();
    }
}

我們在生產DefaultSqlsession實例對象的時候傳入了configuration,而這一步就是從configuration對象中根據statementId取出MappedStatement對象(MappedStatement 封裝了這條SQL的一些信息)

然后執行executor.query()方法,把查詢任務委派給執行器executor

3. executor.query()

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

//↓↓↓↓↓↓↓↓

// 查詢,帶 ResultHandler
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

//↓↓↓↓↓↓↓↓

//此方法在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);
}
@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;
}

queryFromDatabase

// 從數據庫中讀取操作
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;
}

doQuery

@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);
    }
}

prepareStatement()

// 初始化 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;
}

4.StatementHandler

StatementHandler對象主要完成兩個工作:設置參數(prepareStatement),解析返回結果集(resultSetHandler)

對於JDBC的PreparedStatement類型的對象,創建的過程中,我們使用的是SQL語句字符串會包含若干個?占位符,我們其后再對占位符進行設值。

StatementHandler通過parameterize(statement)方法對 Statement 進行設值;

StatementHandler 通過 List query(Statement statement, ResultHandler resultHandler)方法來完成執行Statement,和將Statement對象返回的resultSet封裝成List;

在哪里設置參數?

在prepareStatement方法中有一句話:

handler.parameterize(stmt);

//PreparedStatementHandler.java
@Override
public void parameterize(Statement statement) throws SQLException {
    //使用ParameterHandler對象來完成對Statement的設值
    parameterHandler.setParameters((PreparedStatement) statement);
}
@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);
                }
            }
        }
    }
}

typeHandler.setParameter(ps, i + 1, value, jdbcType);

可以看到,我們實際設置參數是通過parameterHandler設值的。並且調用了 typeHandler.setParameter()方法來給?賦值。同時指定了jdbcType。

回到doQuery()方法,進入到handler.query(stmt, resultHandler)方法,找到其實現類PreparedStatementHandler

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

如何處理結果集?

進入 return resultSetHandler.handleResultSets(ps)

//
// 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 對象,封裝成 ResultSetWrapper 對象
 *
 * @param stmt Statement 對象
 * @return ResultSetWrapper 對象
 */
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
    ResultSet rs = stmt.getResultSet();
    // 可以忽略
    while (rs == null) {
        // move forward to get the first resultset in case the driver
        // doesn't return the resultset as the first result (HSQLDB 2.1)
        if (stmt.getMoreResults()) {
            rs = stmt.getResultSet();
        } else {
            if (stmt.getUpdateCount() == -1) {
                // no more results. Must be no resultset
                break;
            }
        }
    }
    // 將 ResultSet 對象,封裝成 ResultSetWrapper 對象
    return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}
// 處理 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());
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM