Mybatis筆記2-Handler

Handler

參數(shù)處理

Mybatis對(duì)接口實(shí)參的解析發(fā)生在創(chuàng)建Statement之前,對(duì)接口參數(shù)解析結(jié)果如下:

單個(gè)參數(shù) : 直接將調(diào)用接口傳入的實(shí)參返回惕艳,不需要進(jìn)一步解析

多個(gè)參數(shù):多個(gè)參數(shù)的情況下,會(huì)將參數(shù)轉(zhuǎn)換成【參數(shù)名 -> 實(shí)參值】的映射關(guān)系Map
若接口形參使用了@Param參數(shù),使用@Param參數(shù)指定的名稱
若接口形參未使用@Param參數(shù)稚晚,JDK8以上可通過(guò)參數(shù)-parameters打開形參名稱解析,未打開則解析結(jié)果為arg0型诚、arg1...形式的【映射關(guān)系】
Mybatis還會(huì)為每個(gè)接口形參根據(jù)其位置生成一個(gè)兜底的param0客燕、param1...形式的【映射關(guān)系】

ParamNameResolver類的功能是解析Mapper接口入?yún)⒚Q,解析參數(shù)的時(shí)機(jī)為:

public class MapperMethod {
    
    // ...

    private final SqlCommand command;   // 當(dāng)前要操作的SQL標(biāo)簽狰贯,為在Mapper.xml文件中使用的<select><update><delete><insert>標(biāo)簽
    private final MethodSignature method; // 記錄mapper接口命名標(biāo)記信息
    
    public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        switch (command.getType()) {
            // ...
            case SELECT:
                // ...
                // 使用ParamNameResolver開始解析方法調(diào)用的【形參名->實(shí)參值】映射關(guān)系
                // 當(dāng)方法僅有一個(gè)形參時(shí)也搓,param即為方法傳入實(shí)參
                // 當(dāng)方法有多個(gè)形參是,param為Map類型涵紊,記錄【形參名->實(shí)參值】映射關(guān)系
                Object param = method.convertArgsToSqlCommandParam(args);
                // 集成Spring時(shí)傍妒,當(dāng)前的SqlSession對(duì)象為SqlTemplate類對(duì)象
                result = sqlSession.selectOne(command.getName(), param);
                break;
                // ...
            default:
                throw new BindingException("Unknown execution method for: " + command.getName());
        }
        // ...
        return result;
    }
}

解析接口名稱類ParamNameResolver如下:

public class ParamNameResolver {
    
    private static final String GENERIC_NAME_PREFIX = "param";
    private final SortedMap<Integer, String> names;     // 記錄解析得到的入?yún)⒚Q,映射關(guān)系為【參數(shù)位置 -> 參數(shù)名稱】
    private boolean hasParamAnnotation;                 // 標(biāo)記該方法是否使用了@Param注解
    
    // ...
    
    public ParamNameResolver(Configuration config, Method method) {
        // 獲取接口參數(shù)類型數(shù)組
        final Class<?>[] paramTypes = method.getParameterTypes();
        // 獲取接口每個(gè)參數(shù)的注解摸柄,由于參數(shù)前可以采用多個(gè)注解颤练,因此使用二維數(shù)組
        final Annotation[][] paramAnnotations = method.getParameterAnnotations();
        final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
        // 記錄方法參數(shù)個(gè)數(shù),使用paramTypes數(shù)組長(zhǎng)度也一樣驱负,如果沒(méi)有被注解修飾的參數(shù)也會(huì)作為一個(gè)空數(shù)組放入這個(gè)二維數(shù)組
        int paramCount = paramAnnotations.length;
        // get names from @Param annotations
        for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
            // 若當(dāng)前參數(shù)類型為RowBounds或者ResultHandler不處理
            if (isSpecialParameter(paramTypes[paramIndex])) {
                continue;
            }
            String name = null;
            // 遍歷每一個(gè)參數(shù)的注解嗦玖,查詢是否使用了@Param注解,獲取@Param上面的name屬性
            for (Annotation annotation : paramAnnotations[paramIndex]) {
                if (annotation instanceof Param) {
                    hasParamAnnotation = true;
                    name = ((Param) annotation).value();
                    break;
                }
            }
            if (name == null) {
                if (config.isUseActualParamName()) {
                    // 將入?yún)⒔馕鰹閍rg0跃脊、arg1格式的名稱
                    // jdk8(含)以上版本宇挫,可以在編譯類的時(shí)候使用 -parameters 命令,那么就可以獲取方法參數(shù)的名稱
                    name = getActualParamName(method, paramIndex);
                }
                if (name == null) {
                    // 直接使用數(shù)字0,1,2作為入?yún)⒚Q
                    name = String.valueOf(map.size());
                }
            }
            map.put(paramIndex, name);
        }
        names = Collections.unmodifiableSortedMap(map);
    }
    
    /**
     * 解析接口參數(shù)名稱
     * @param args 調(diào)用本接口的實(shí)參 
     * @return 使用Object類型返回酪术,方法僅有一個(gè)形參時(shí)返回入?yún)?duì)象捞稿,有多個(gè)形參是,返回【形參名->實(shí)參值】映射關(guān)系Map對(duì)象
     **/
    public Object getNamedParams(Object[] args) {
        final int paramCount = names.size();
        // 如果調(diào)用接口實(shí)參為null 或 從接口上解析得到的參數(shù)個(gè)數(shù)為0
        if (args == null || paramCount == 0) {
            return null;
        } 
        // 如果沒(méi)有使用@Param注解且參數(shù)就一個(gè)拼缝,直接返回傳入的實(shí)參即可
        else if (!hasParamAnnotation && paramCount == 1) {  
            return args[names.firstKey()];
        } else {
            final Map<String, Object> param = new ParamMap<Object>();
            int i = 0;
            for (Map.Entry<Integer, String> entry : names.entrySet()) {
                // 保存參數(shù)名稱 -> 實(shí)參值 映射關(guān)系
                param.put(entry.getValue(), args[entry.getKey()]);
                // 生成一個(gè)當(dāng)前位置參數(shù)名稱娱局,格式為param1.....
                final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
                // 若不存在這個(gè)格式的參數(shù)名稱,額外保存param1 -> 實(shí)參值 的映射關(guān)系
                if (!names.containsValue(genericParamName)) {
                    param.put(genericParamName, args[entry.getKey()]);
                }
                i++;
            }
            // 最終返回的一定有param1 -> 實(shí)參值的映射
            // 以下兩種映射每個(gè)參數(shù)必存在一種
            // arg0 -> 實(shí)參
            // @Param注解定義名稱 -> 實(shí)參
            return param;
        }
    }
}

StatementHandler

JDBC處理器咧七,基于JDBC構(gòu)建Statement并設(shè)置參數(shù)衰齐,然后執(zhí)行SQL。每調(diào)用會(huì)話當(dāng)中一次SQL继阻,都會(huì)有與之相對(duì)應(yīng)的且唯一的Statement實(shí)例耻涛。

image-20210507161009434

PreparedStatementHandler

相對(duì)來(lái)說(shuō)废酷,PreparedStatement的應(yīng)用場(chǎng)景是所有Statement中應(yīng)用最多的,這里以其對(duì)應(yīng)的PreparedStatementHandler為例進(jìn)行學(xué)習(xí)抹缕。

image-20210507161814048
創(chuàng)建PreparedStatement
public class SimpleExecutor extends BaseExecutor {
    
    // ...
    
    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, 
                               ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        
        Configuration configuration = ms.getConfiguration();
        // 通過(guò)Configuration創(chuàng)建StatementHandler澈蟆,然后由StatementHandler創(chuàng)建Statement
        // 這里以PreparedStatementHandler創(chuàng)建PreparedStatement為例
        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, 
                                                                     resultHandler, boundSql);
        // 擁有了PreparedStatementHandler之后,創(chuàng)建PreparedStatement
        Statement stmt = prepareStatement(handler, ms.getStatementLog());
        return handler.query(stmt, resultHandler);
    }
    
    // 通過(guò)PreparedStatementHandler之后卓研,創(chuàng)建PreparedStatement
    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        Connection connection = getConnection(statementLog);
        // 通過(guò)BaseStatementHandler創(chuàng)建PreparedStatement
        stmt = handler.prepare(connection, transaction.getTimeout());
        // 為PreparedStatement的占位符?設(shè)置參數(shù)
        handler.parameterize(stmt);
        return stmt;
    }
}

// 展示SimpleExecutor類中通過(guò)Configuration創(chuàng)建PreparedStatement的相關(guān)方法
public class Configuration {
    
    // ...
    
    // 通過(guò)Configuration創(chuàng)建StatementHandler
    public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, 
                                                Object parameterObject, RowBounds rowBounds, 
                                                ResultHandler resultHandler, BoundSql boundSql) {
        
        StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, 
                                                                        rowBounds, resultHandler, boundSql);
        statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
        return statementHandler;
    }
    
}

// Configuration通過(guò)RoutingStatementHandler創(chuàng)建不同種類的StatementHandler趴俘,主要根據(jù)MappedStatement配置屬性StatementType決定
public class RoutingStatementHandler implements StatementHandler {
    
    private final StatementHandler delegate;
    
    public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, 
                                   ResultHandler resultHandler, BoundSql boundSql) {
        // 根據(jù)MappedStatement的配置信息,選擇創(chuàng)建不同的Statement
        switch (ms.getStatementType()) {
            case STATEMENT:
                delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                break;
            case PREPARED:
                delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                break;
            case CALLABLE:
                delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                break;
            default:
                throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
        }
    }
    
    // ...
}

// PreparedStatementHandler創(chuàng)建PreparedStatement對(duì)象奏赘,通過(guò)調(diào)用父類BaseStatementHandler抽取的共同方法prepare實(shí)現(xiàn)
public abstract class BaseStatementHandler implements StatementHandler {
    
    // 創(chuàng)建Statement對(duì)象
    public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        ErrorContext.instance().sql(boundSql.getSql());
        Statement statement = null;
        try {
            // instantiateStatement是個(gè)抽象方法寥闪,由子類StatementHandler、PreparedStatementHandler磨淌、CallableStatementHandler實(shí)現(xiàn)
            statement = instantiateStatement(connection);
            setStatementTimeout(statement, transactionTimeout);
            setFetchSize(statement);
            return statement;
        } catch (SQLException e) {
            // ...
        } catch (Exception e) {
            // ...
        }
    }
    
}


public class PreparedStatementHandler extends BaseStatementHandler {
    
    // ...
    
    // 實(shí)現(xiàn)父類BaseStatementHandler中的抽象方法instantiateStatement疲憋,創(chuàng)建PreparedStatement
    protected Statement instantiateStatement(Connection connection) throws SQLException {
        String sql = boundSql.getSql();
        if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
            String[] keyColumnNames = mappedStatement.getKeyColumns();
            if (keyColumnNames == null) {
                return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
            } else {
                return connection.prepareStatement(sql, keyColumnNames);
            }
        } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
            return connection.prepareStatement(sql);
        } else {
            return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
        }
    }
}

ParameterHandler

通過(guò)TypeHandler為PreparedStatement設(shè)置參數(shù)
public class ReuseExecutor extends BaseExecutor {

    // ...
    
    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
        Statement stmt;
        Connection connection = getConnection(statementLog);
        stmt = handler.prepare(connection, transaction.getTimeout());
        // 創(chuàng)建好PreparedStatement之后,為其設(shè)置參數(shù)
        handler.parameterize(stmt);
        return stmt;
    }

}

public class PreparedStatementHandler extends BaseStatementHandler {
 
    // ...
    
    public void parameterize(Statement statement) throws SQLException {
        parameterHandler.setParameters((PreparedStatement) statement);
    }
}

// 設(shè)置參數(shù)處理器梁只,有且僅有此實(shí)現(xiàn)
public class DefaultParameterHandler implements ParameterHandler {
    
    // ...
    
    public void setParameters(PreparedStatement ps) {
        // 得到Sql的相關(guān)參數(shù)信息缚柳,內(nèi)含TypeHandler,JdbcType搪锣,JavaType
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        if (parameterMappings != null) {
            for (int i = 0; i < parameterMappings.size(); i++) {
                ParameterMapping parameterMapping = parameterMappings.get(i);
                // 不是存儲(chǔ)過(guò)程的OUT參數(shù)
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    Object value;
                    String propertyName = parameterMapping.getProperty();
                    if (boundSql.hasAdditionalParameter(propertyName)) { 
                        value = boundSql.getAdditionalParameter(propertyName);
                    } 
                    // Mapper接口沒(méi)有形參秋忙,也就沒(méi)有實(shí)參,直接將要設(shè)置的value值賦值為null
                    else if (parameterObject == null) {
                        value = null;
                    } 
                    // 檢查是否有TypeHandler可以處理當(dāng)前接口實(shí)參類型淤翔,符合這個(gè)方法的話表示當(dāng)前接口方法僅有一個(gè)形參且為基本類型翰绊,
                    // 否則parameterObject為Map類型
                    // TypeHandlerRegistry記錄了 JavaType -> JdbcType -> TypeHandler 的映射關(guān)系,
                    // 存在 JavaType -> null -> TypeHandler 的映射關(guān)系旁壮,表示該java類型沒(méi)有指定jdbc類型默認(rèn)使用的TypeHandler
                    else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                        value = parameterObject;
                    } else {
                        // TODO 這個(gè)metaObject對(duì)象為什么不直接作為本方法的全局變量????
                        // TODO 如果parameterObject為Map<String,Object>類型监嗜、非簡(jiǎn)單類型,不是每次都需要重新解析???
                        
                        // MetaObject可以通過(guò)反射獲取Map<String,Object>以及用戶自定義對(duì)象屬性的值
                        MetaObject metaObject = configuration.newMetaObject(parameterObject);
                        value = metaObject.getValue(propertyName);
                    }
                    TypeHandler typeHandler = parameterMapping.getTypeHandler();
                    JdbcType jdbcType = parameterMapping.getJdbcType();
                    if (value == null && jdbcType == null) {
                        jdbcType = configuration.getJdbcTypeForNull();
                    }
                    try {
                        // 通過(guò)TypeHandler為PreparedStatement設(shè)置占位符實(shí)參
                        typeHandler.setParameter(ps, i + 1, value, jdbcType);
                    } catch (TypeException | SQLException e) {
                        // ...
                    }
                }
            }
        }
    }
}

ResultSetHandler

執(zhí)行數(shù)據(jù)庫(kù)查詢抡谐,處理返回的結(jié)果集

public class SimpleExecutor extends BaseExecutor {
    
    // ...
    
    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();
            StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, 
                                                                         resultHandler, boundSql);
            stmt = prepareStatement(handler, ms.getStatementLog());
            // 通過(guò)PreparedStatementHandler執(zhí)行數(shù)據(jù)庫(kù)查詢操作
            return handler.query(stmt, resultHandler);
        } finally {
            // 關(guān)閉PreparedStatement
            closeStatement(stmt);
        }
    }
}

// 通過(guò)PreparedStatementHandler觸發(fā)數(shù)據(jù)庫(kù)操作
public class PreparedStatementHandler extends BaseStatementHandler {

    // ...
    
    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        // 調(diào)用JDBC的PreparedStatement#execute執(zhí)行數(shù)據(jù)庫(kù)操作
        ps.execute();
        // 使用ResultSetHandler處理返回的結(jié)果集ResultSet
        return resultSetHandler.handleResultSets(ps);
    }
}

// 使用ResultSetHandler處理返回結(jié)果集ResultSet
public class DefaultResultSetHandler implements ResultSetHandler {
 
    // ...
    
    public List<Object> handleResultSets(Statement stmt) throws SQLException {
        
        final List<Object> multipleResults = new ArrayList<>();
        
        int resultSetCount = 0;
        // 獲取第一個(gè)結(jié)果集裁奇,由于存儲(chǔ)過(guò)程可能有多個(gè)出參,存在多個(gè)結(jié)果集返回的情況
        ResultSetWrapper rsw = getFirstResultSet(stmt);
        
        List<ResultMap> resultMaps = mappedStatement.getResultMaps();
        int resultMapCount = resultMaps.size();
        validateResultMapsCount(rsw, resultMapCount);
        while (rsw != null && resultMapCount > resultSetCount) {    
            ResultMap resultMap = resultMaps.get(resultSetCount);
            // 開始處理結(jié)果集(下有源碼細(xì)節(jié))
            handleResultSet(rsw, resultMap, multipleResults, null);
            // 處理下一個(gè)結(jié)果集麦撵,存儲(chǔ)過(guò)程用的很少刽肠,這里大多數(shù)時(shí)候都是僅僅處理一個(gè)結(jié)果集就好了
            rsw = getNextResultSet(stmt);
            cleanUpAfterHandlingResultSet();
            resultSetCount++;
        }
        // ...
        // 由于存在存儲(chǔ)過(guò)程有多個(gè)結(jié)果集的情況,multipleResults可能是List<List<Object>>集合嵌套集合類型免胃,
        // 如果僅僅一個(gè)結(jié)果集的處理結(jié)果音五,直接返回第一個(gè)元素List<Object>結(jié)果即可
        return collapseSingleResultList(multipleResults);
    }
    
    // 處理結(jié)果集
    private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, 
                                 ResultMapping parentMapping) throws SQLException {
        try {
            if (parentMapping != null) {
                handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
            } else {
                if (resultHandler == null) {
                    // 使用ResultHandler(注意區(qū)分ResultSetHandler)解析好的結(jié)果
                    // DefaultResultHandler在下文有源碼展示,其實(shí)就是簡(jiǎn)單的使用了一個(gè)List集合保存解析的每一行結(jié)果集結(jié)果
                    DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
                    // 開始處理結(jié)果集的每一行數(shù)據(jù)(下有源碼細(xì)節(jié))
                    handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
                    // 從ResultHandler保存的結(jié)果List中獲取數(shù)據(jù)羔沙,放入multipleResults集合作為參數(shù)返回
                    multipleResults.add(defaultResultHandler.getResultList());
                } else {
                    handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
                }
            }
        } finally {
            // 關(guān)閉ResultSet結(jié)果集
            closeResultSet(rsw.getResultSet());
        }
    }
    
    // 開始將ResultSet結(jié)果集的每一行處理為對(duì)    象返回
    public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, 
                                RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
        // 判斷當(dāng)前方法的ResultMap是否嵌套了另一個(gè)ResultMap躺涝,比如:
        /* <resultMap id="resultMap" type="doman實(shí)體" extends="BaseResultMap">
               <collection property="c" resultMap="otherBaseResultMap"/>
           </resultMap>
         */
        if (resultMap.hasNestedResultMaps()) {
            ensureNoRowBounds();
            checkResultHandler();
            handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
        } else {
            // 暫時(shí)不去探究嵌套R(shí)esultMap處理,先來(lái)看單個(gè)ResultMap如何處理的(下有源碼細(xì)節(jié))
            handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
        }
    }
    
    // 針對(duì)單一ResultMap的情況進(jìn)行結(jié)果集解析
    private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, 
                                                   ResultHandler<?> resultHandler, RowBounds rowBounds, 
                                                   ResultMapping parentMapping) throws SQLException {
        
        // ResultContext類可以看做是解析ResultSet過(guò)程的一個(gè)上下文對(duì)象扼雏,用來(lái)記錄當(dāng)前最新解析結(jié)果集行結(jié)果坚嗜,記錄解析行數(shù)夯膀,是否達(dá)到分頁(yè)條目停止解析
        DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
        // 分頁(yè)查詢時(shí),將結(jié)果集偏移量調(diào)整到分頁(yè)開始行
        skipRows(rsw.getResultSet(), rowBounds);
        // 條件1 : 通過(guò)ResultContext當(dāng)前是否設(shè)置了停止解析 或 到達(dá)分頁(yè)條目  不再進(jìn)一步解析結(jié)果集(下有源碼細(xì)節(jié))
        // 條件2 : 結(jié)果集不再有下一行
        while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
            ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
            // rowValue表示一個(gè)解析成ResultMap指定類型的一行結(jié)果集數(shù)據(jù)(下有源碼細(xì)節(jié))
            Object rowValue = getRowValue(rsw, discriminatedResultMap);
            // 將這一行結(jié)果集處理的結(jié)果保存到ResultHandler和ResultContext(下有源碼細(xì)節(jié))
            storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
        }
    }
    
    // 通過(guò)ResultContext和RowBounds判斷是否需要繼續(xù)解析結(jié)果集ResultSet
    private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) throws SQLException {
        return !context.isStopped() && context.getResultCount() < rowBounds.getLimit();
    }
    
    // 存儲(chǔ)解析得到的一行ResultSet結(jié)果
    private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, 
                             Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
        if (parentMapping != null) {
            linkToParents(rs, parentMapping, rowValue);
        } else {
            // 存儲(chǔ)結(jié)果(下有源碼細(xì)節(jié))
            callResultHandler(resultHandler, resultContext, rowValue);
        }
    }
    
    // 存儲(chǔ)ResultSet每一行處理結(jié)果
    private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, 
                                   Object rowValue) {
        // ResultContext保存每一次最后解析得到的對(duì)象苍蔬,并記錄解析過(guò)多少行
        resultContext.nextResultObject(rowValue);
        // 將每一行ResultSet結(jié)果處理結(jié)果存放到ResultHandler的屬性集合中(該類下有DefaultResultHandler實(shí)現(xiàn)源碼細(xì)節(jié))
        ((ResultHandler<Object>) resultHandler).handleResult(resultContext);
    }
    
    // 解析結(jié)果集ResultSet一行數(shù)據(jù)诱建,封裝成目標(biāo)類型返回
    private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
        final ResultLoaderMap lazyLoader = new ResultLoaderMap();
        // 創(chuàng)建一個(gè)目標(biāo)類型的空對(duì)象(下有源碼細(xì)節(jié))
        Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
        
        if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
            final MetaObject metaObject = configuration.newMetaObject(rowValue);
            boolean foundValues = this.useConstructorMappings;
            // <rusultMap id="ddd" type="user" autoMapping="true">,可以通過(guò)如上方法碟绑,指定ResultMap采用自動(dòng)映射機(jī)制
            if (shouldApplyAutomaticMappings(resultMap, false)) {
                foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
            }
            // 手動(dòng)映射返回結(jié)果屬性俺猿,較為復(fù)雜,需要處理嵌套R(shí)esultMap蜈敢,嵌套子查詢的情況(下有源碼細(xì)節(jié))
            foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
            foundValues = lazyLoader.size() > 0 || foundValues;
            rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
        }
        return rowValue;
    }
    
    // 創(chuàng)建ResultMap指定的返回對(duì)象類型
    private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, 
                                      String columnPrefix) throws SQLException {
        this.useConstructorMappings = false; 
        final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
        final List<Object> constructorArgs = new ArrayList<Object>();
        // 具體創(chuàng)建返回對(duì)象方法(下有源碼細(xì)節(jié))
        /*
            ResultMap標(biāo)簽可以指定創(chuàng)建返回類的構(gòu)造器辜荠,如下:
            <resultMap id="RoleResultMap" type="RoleDTO">
                <constructor>
                    <arg column="user_id"/>
                </constructor>
            </resultMap>
        */
        Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
        
        if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
            final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
            for (ResultMapping propertyMapping : propertyMappings) {
                // 嵌套查詢?yōu)槿缦虑闆r:
                /* <resultMap id="ddd" type="user">
                        <asoociation property="author" select="selectByUserId">
                   </resultMap>
                 */
                if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
                    resultObject = configuration.getProxyFactory().createProxy(resultObject, 
                                                                               lazyLoader, configuration, objectFactory, 
                                                                               constructorArgTypes, constructorArgs);
                    break;
                }
            }
        }
        this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty();
        return resultObject;
    }
    
    // 根據(jù)ResultMap創(chuàng)建每一行返回的空對(duì)象
    private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, 
                                      List<Object> constructorArgs, String columnPrefix) throws SQLException {
        // 獲取ResultMap的返回類型
        final Class<?> resultType = resultMap.getType();
        // 解析返回類型的元數(shù)據(jù)信息
        final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
        final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
        // 如果是原始類型汽抚,直接使用TypeHandler處理數(shù)據(jù)返回
        if (hasTypeHandlerForResultObject(rsw, resultType)) {
            return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
        } 
        // 如果ResultMap存在構(gòu)造器映射配置抓狭,則通過(guò)構(gòu)造器創(chuàng)建
        else if (!constructorMappings.isEmpty()) {
            return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, 
                                                   constructorArgs, columnPrefix);
        } 
        // 如果返回類型是個(gè)接口,或者存在默認(rèn)構(gòu)造器造烁,直接創(chuàng)建一個(gè)空對(duì)象
        else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
            return objectFactory.create(resultType);
        } 
        // 基于自動(dòng)映射否过,自動(dòng)一依次查找是否于指定構(gòu)造方法匹配,若有自動(dòng)創(chuàng)建對(duì)象
        else if (shouldApplyAutomaticMappings(resultMap, false)) {
            return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);
        }
        throw new ExecutorException("Do not know how to create an instance of " + resultType);
    }
    
    private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, 
                                          ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
        
        final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
        boolean foundValues = false;
        final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
        // TODO 只要有一個(gè)屬性填充上就表示可以返回該對(duì)象?????????
        for (ResultMapping propertyMapping : propertyMappings) {
            String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
            if (propertyMapping.getNestedResultMapId() != null) {
                column = null;
            }
            if (propertyMapping.isCompositeResult()
                    || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
                    || propertyMapping.getResultSet() != null) {
                // 存在嵌套子查詢的處理
                Object value = 
                    getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, 
                                                       lazyLoader, columnPrefix);
                final String property = propertyMapping.getProperty();
                if (property == null) {
                    continue;
                } else if (value == DEFERED) {
                    foundValues = true;
                    continue;
                }
                if (value != null) {
                    foundValues = true;
                }
                if (value != null 
                        || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
                    metaObject.setValue(property, value);
                }
            }
        }
        return foundValues;
    }
    
    private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, 
                                           ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
        /* <resultMap id="ddd" type="user">
                <asoociation property="author" select="selectByUserId">
           </resultMap>
        */
        if (propertyMapping.getNestedQueryId() != null) {
            return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
        } 
         /* <resultMap id="ddd" type="user">
                <asoociation property="author" resultSet="ss" select="selectByUserId">
           </resultMap>
        */
        else if (propertyMapping.getResultSet() != null) {
            addPendingChildRelation(rs, metaResultObject, propertyMapping);   // TODO is that OK?
            return DEFERED;
        } else {
            final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
            final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
            return typeHandler.getResult(rs, column);
        }
    }
}

補(bǔ)充:使用ResultContext控制解析結(jié)果集數(shù)量例子

ResultHandler范例

ResultHandler

使用一個(gè)list保存解析ResultSet每一行的結(jié)果

public class DefaultResultHandler implements ResultHandler<Object> {
    private final List<Object> list;
    
    public DefaultResultHandler() {
        list = new ArrayList<Object>();
    }
    
    public DefaultResultHandler(ObjectFactory objectFactory) {
        list = objectFactory.create(List.class);
    }
    
    @Override
    public void handleResult(ResultContext<? extends Object> context) {
        list.add(context.getResultObject());
    }
    
    public List<Object> getResultList() {
        return list;
    }
}

附錄

MapperMethod與SqlSessionTemplate的關(guān)系

Mybatis集成Spring后惭蟋,通過(guò)向Spring注冊(cè)MapperFactoryBean生產(chǎn)Mapper接口的代理對(duì)象苗桂,每個(gè)代理對(duì)象都會(huì)持有SqlSessionTemplate對(duì)象,當(dāng)程序觸發(fā)代理對(duì)象的方法時(shí)告组,JDK動(dòng)態(tài)代理攔截方法調(diào)用煤伟,會(huì)通過(guò)MepperMethod#execute調(diào)用SqlSessionTemplate方法,而SqlSessionTemplate又會(huì)將查詢委托給自己封裝的另一個(gè)代理sqlSessionProxy木缝。

image-20210507220144214
public class MapperProxyFactory<T> {
    
    // ...
    
    // 要生成代理對(duì)的接口
    private final Class<T> mapperInterface;
    // 
    private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
    
    
    // 生成Mapper接口的代理對(duì)象
    protected T newInstance(MapperProxy<T> mapperProxy) {
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
    }
    
    public T newInstance(SqlSession sqlSession) {
        final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
    }

}

public class MapperProxy<T> implements InvocationHandler, Serializable {

    // ...
    
    private final SqlSession sqlSession;    // 集成Mybatis便锨,該屬性注入SqlSessionTemplate對(duì)象
    
    // 調(diào)用Mapper接口代理對(duì)象方法,會(huì)被該方法攔截
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            } else if (isDefaultMethod(method)) {
                return invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        return mapperMethod.execute(sqlSession, args);
    }
}
public class SqlSessionTemplate implements SqlSession, DisposableBean {
    private final SqlSessionFactory sqlSessionFactory;
    private final ExecutorType executorType;
    private final SqlSession sqlSessionProxy;
    private final PersistenceExceptionTranslator exceptionTranslator;
    
    public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
                              PersistenceExceptionTranslator exceptionTranslator) {
        
        this.sqlSessionFactory = sqlSessionFactory;
        this.executorType = executorType;
        this.exceptionTranslator = exceptionTranslator;
        // 創(chuàng)建代理對(duì)象
        this.sqlSessionProxy = (SqlSession) newProxyInstance(
            SqlSessionFactory.class.getClassLoader(),
            new Class[] { SqlSession.class },
            new SqlSessionInterceptor());
    }
    
    // SqlSessionTemplate內(nèi)部的方法都委托給另一個(gè)代理對(duì)象sqlSessionProxy
    public <T> T selectOne(String statement, Object parameter) {
        return this.sqlSessionProxy.<T> selectOne(statement, parameter);
    }
    
    // ... 
}

查詢流程圖

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末我碟,一起剝皮案震驚了整個(gè)濱河市放案,隨后出現(xiàn)的幾起案子禀苦,更是在濱河造成了極大的恐慌蠢甲,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件珊拼,死亡現(xiàn)場(chǎng)離奇詭異厘托,居然都是意外死亡友雳,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門铅匹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)押赊,“玉大人,你說(shuō)我怎么就攤上這事伊群】忌迹” “怎么了策精?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)崇棠。 經(jīng)常有香客問(wèn)我咽袜,道長(zhǎng),這世上最難降的妖魔是什么枕稀? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任询刹,我火速辦了婚禮,結(jié)果婚禮上萎坷,老公的妹妹穿的比我還像新娘凹联。我一直安慰自己,他們只是感情好哆档,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布蔽挠。 她就那樣靜靜地躺著,像睡著了一般瓜浸。 火紅的嫁衣襯著肌膚如雪澳淑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天插佛,我揣著相機(jī)與錄音杠巡,去河邊找鬼。 笑死雇寇,一個(gè)胖子當(dāng)著我的面吹牛氢拥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播锨侯,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼嫩海,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了识腿?” 一聲冷哼從身側(cè)響起出革,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎渡讼,沒(méi)想到半個(gè)月后骂束,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡成箫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年展箱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蹬昌。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡混驰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情栖榨,我是刑警寧澤昆汹,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站婴栽,受9級(jí)特大地震影響满粗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜愚争,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一映皆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧轰枝,春花似錦捅彻、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至湾戳,卻和暖如春贤旷,著一層夾襖步出監(jiān)牢的瞬間广料,已是汗流浹背砾脑。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留艾杏,地道東北人韧衣。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像购桑,于是被迫代替她去往敵國(guó)和親畅铭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容