手敲Mybatis(九)-结果集处理器

news2024/11/27 21:53:54

1.前言-背景介绍

上节我们处理了参数处理器,本节我们处理结果集处理器,之前我们写了一个DefaultResultSetHandler,我们把返回结果获取对象,填充值什么的写到了一起,流程没有进行解耦,并且只接收了Object的对象值类型,需要多元化,所以本节也要解决按返回类型从结果集中获取对应的结果类型值。

 2.设计说明

2.1.结果映射ResultMap

我们要得到ResultType和ResultMap(本节先处理ResultType)才能知道用户最终要的是什么对象,当然定位还是要靠namespace,这时候我们需要一个类ResultMap结果映射类,内容有id(namespace+其他内容拼接)以及结果类型,还有具体结果的映射字段信息以及定义已映射字段如下第二个图片

 我们看到ResultMap里有ResultMapping,这就是结果集映射里每一个字段信息(本节暂时没用上,只做定义),可以看到定义了属性,列名,java类型,jdbc类型,类型处理器

2.2 MapperBuilderAssistant助手类

为什么加这个类呢?

刚才我们说除了结果的映射构建需要namespce,其实前面章节参数也需要用到这里,而参数映射结果集都需要映射,有一些共通性,所以我们新增加一个类MapperBuilderAssistant(构建mapper助手)去帮助我们构建Mapper映射,目前参数和结果都在此类映射处理。

2.3 ResultSetHandler结果集处理

结果集处理器按字面理解就是将JDBC得到的结果集数据在此处理,最后得到返回对象结果,想要返回配置的对象而且有值,就必须解析xml中的resultType并实例化对象,并从JDBC数据集里获取属性名称,根据属性名称反射设置值并将有值的返回对象存储到结果处理器里并返回,大致的设计业务就是这样处理的。

结果包装器ResultSetWrapper

结果包装器的设计理念是想要将结果集的一些基本信息的逻辑处理到此类里,如JDBC的结果集呀,属性名称啊,jdbc类型,已映射的属性数据、未映射的属性等等处理,为结果集处理器提供数据支持。

结果上下文DefaultResultContext

结果上下文设计这个主要是每一行数据对象要存储到此类全局变量里。

结果处理器DefaultResultHandler

结果处理器设计为将每一个上下文的对象结果数据存储到List里,就可支持多个数据了。

3.UML类图

依旧和参数处理器的思路一样,分为构建处理周期以及使用周期,构建是从解析XML配置开始的,使用则是执行了数据库操作以后我们要用到处理结果的一系列流程。

大家可以看下,涉及新增的类不多,也都比较简单可以看看,其中UML中的关系MapperStatement和XmlStatementBuilder属于聚合关系,聚合关系就是比依赖要强一些的关系,整体与部分,MapperStatement就是XmlStatementBuilder的一部分协助处理映射功能的。

而ResultMapping和ResultMap就是组合关系,组合关系则要比聚合关系关联性还要强,组合则是整体与部分不分开,同根同源,同生命周期,这里呢resultMapping与ResultMap就是一个生命周期,有了ResultMapping才有ResultMap。

 4.代码实现

4.1 解析构建结果映射

4.1.1 结果映射属性封装

ResultMap,结果映射,属性关于namespce和返回结果类型以及返回具体的属性信息

public class ResultMap {
    private String id;
    private Class<?> type;
    private List<ResultMapping> resultMappings;
    private Set<String> mappedColumns;

    public ResultMap() {
    }

    public static class Builder {
        private ResultMap resultMap = new ResultMap();

        public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> resultMappings) {
            resultMap.id = id;
            resultMap.type = type;
            resultMap.resultMappings = resultMappings;
        }

        public ResultMap build() {
            resultMap.mappedColumns = new HashSet<>();
            return resultMap;
        }
    }

    public String getId() {
        return id;
    }

    public Class<?> getType() {
        return type;
    }

    public List<ResultMapping> getResultMappings() {
        return resultMappings;
    }

    public Set<String> getMappedColumns() {
        return mappedColumns;
    }
}

ResultMapping,具体的映射属性数据,暂时内容还没有用到,现在代码先写上

/**
 * @Author df
 * @Date 2023/3/28 12:38
 * @Version 1.0
 * 结果映射的每一个字段的信息
 */
public class ResultMapping {
    private Configuration configuration;
    private String property;
    private String column;
    private Class<?> javaType;
    private JdbcType jdbcType;
    private TypeHandler<?> typeHandler;

    ResultMapping() {

    }

    public static class Builder {
        private ResultMapping resultMapping = new ResultMapping();
    }
}

 4.1.2 绑定映射结果

MapperBuilderAssistant,协助XmlStatementBuilder去处理Mapper的映射,这样在XmlStatementBuilder就可以去掉映射这部分代码,专心处理解析,只需调用映射方法即可,其中映射返回结果的数据在setStatementResultMap方法中,最后得到的ResultMap对象存储到MappedStatement里

public class MapperBuilderAssistant extends BaseBuilder {
    private String currentNamespace;
    private String resource;

    public MapperBuilderAssistant(Configuration configuration, String resource) {
        super(configuration);
        this.resource = resource;
    }

    public String getCurrentNamespace() {
        return currentNamespace;
    }

    public void setCurrentNamespace(String currentNamespace) {
        this.currentNamespace = currentNamespace;
    }
    
    /**
     * 将当前namespace和id字符串进行拼接
     * */
    public String applyCurrentNamespace(String base, Boolean isReferent) {
        if (base == null)
            return null;
        if (isReferent) {
            if (base.contains(".")) return base;
        }
        return currentNamespace + "." + base;
    }

    /**
     * 添加映射器语句
     */
    public MappedStatement addMapperStatement(
            String id,
            SqlSource sqlSource,
            SqlCommandType sqlCommandType,
            Class<?> parameterType,
            String resultMap,
            Class<?> resultType,
            LanguageDriver lang
    ) {
        id = applyCurrentNamespace(id, false);
        MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlCommandType, sqlSource, resultType);

        setStatementResultMap(resultMap, resultType, statementBuilder);

        MappedStatement mappedStatement = statementBuilder.build();
        configuration.addMappedStatement(mappedStatement);
        return mappedStatement;
    }

    // 封装ResultMap对象
    private void setStatementResultMap(
            String resultMap,
            Class<?> resultType,
            MappedStatement.Builder statementBuilder
    ) {
        resultMap = applyCurrentNamespace(resultMap, true);
        List<ResultMap> resultMaps = new ArrayList<>();

        if (resultMap != null) {
             // 暂时先不处理resultMap情况
        } else if (resultType != null) {
            // 适配一下resultType也封装处理为ResultMap
            ResultMap.Builder inlineResultMapBuilder = new ResultMap.Builder(configuration,
                    statementBuilder.id() + "-Inline",
                    resultType,
                    new ArrayList());
            resultMaps.add(inlineResultMapBuilder.build());
        }
        statementBuilder.resultMaps(resultMaps);
    }
}

4.1.3 调用映射助手类 

   XMLStatementBuilder,类里引用MapperBuilderAssistant并且构造方法参数传入赋值,这样解析完数据直接调用助手类进行绑定。

public class XMLStatementBuilder extends BaseBuilder {
    private MapperBuilderAssistant builderAssistant;
    // 省略其他

    public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, Element element) {
        super(configuration);
        this.element = element;
        this.builderAssistant = builderAssistant;
    }
    
     // 开始解析select标签的id,参数,返回值,并调用语言驱动器
    public void parseStatementNode() {

        // 省略其他
        
        // 调用MapperBuilderAssistant助手类帮助完成mapper的绑定映射
        builderAssistant.addMapperStatement(
                id,
                sqlSource,
                sqlCommandType,
                parameterTypeClass,
                resultMap,
                resultTypeClass,
                langDriver);
    }
}

4.1.4 创建映射助手类

XMLMapperBuilder修改的地方,一个是namespce的处理,一个是获取到MapperBuilderAssistant传递到XmlStatementBuilder中,修改点大致如下

public class XMLMapperBuilder extends BaseBuilder {
     
    // 省略...
    // 映射器构建助手
    private MapperBuilderAssistant builderAssistant;

    public XMLMapperBuilder(Document document, Configuration configuration, String resource) {
        super(configuration);
        // 省略...
        // 创建构建mapper助手类
        this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
    }
   
    private void configurationElement(Element element) {
        // 配置namespace
        String namespace = element.attributeValue("namespace");
        builderAssistant.setCurrentNamespace(namespace);
        // 省略...
    }

}

 MappedStatement修改的地方,主要添加了ResultMap的存储以List的形式,最后在Builder内部类里的resultMaps赋值即可

public class MappedStatement {
   // 省略...
   private List<ResultMap> resultMaps;
   
   public static class Builder {
         // 省略...
         public Builder resultMaps(List<ResultMap> resultMaps) {
            mappedStatement.resultMaps = resultMaps;
            return this;
        }
   }
}

4.2 使用映射处理返回结果 

4.2.1 结果集处理器

构建完了我们需要的返回值映射,如果执行了Sql语句开始处理结果,可以知道JDBC返回的是结果集,需要处理这样的对象数据,所以定义一个结果集处理接口,看过之前的章节的朋友就会知道,这个接口的定义之前就是有的,我们这次大改的主要是它的实现类,可以接着往下看。

/**
 * @Author df
 * @Date 2022/6/13 14:26
 * @Version 1.0
 * @description 结果集处理器
 */
public interface ResultSetHandler {
    <E> List<E> handleResultSets(Statement statement) throws SQLException;
}

它的实现类里则耦合度极高并没有职责分离,现在则分出来结果集包装器,结果处理器,结果上下文供此方法使用,它结果集内容如属性类型则由结果包装器进来拆解组装,结果集处理器则是根据MappedStatement的ResultMap里的type类型进行反射获取对象,然后通过结果包装器里解析的数据反射到对象设置属性,然后将已处理完的对象存储到结果上下文中,最后每一个结果上下文都存储到结果处理器中返回,供调用者获取结果对象。

handleResultSets(),这个方法执行调用流程,可以打断点仔细梳理,调用链比较多,但都是本方法内的,把每一个调用链条上的都梳理通了也就不难了,

public class DefaultResultSetHandler implements ResultSetHandler {
    private final BoundSql boundSql;
    private final MappedStatement mappedStatement;
    private final Configuration configuration;
    private final RowBounds rowBounds;
    private final ResultHandler resultHandler;
    private final TypeHandlerRegistry typeHandlerRegistry;
    private final ObjectFactory objectFactory;

    public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatement, ResultHandler resultHandler, RowBounds rowBounds, BoundSql boundSql) {
        this.boundSql = boundSql;
        this.mappedStatement = mappedStatement;
        this.configuration = mappedStatement.getConfiguration();
        this.rowBounds = rowBounds;
        this.resultHandler = resultHandler;
        this.objectFactory = configuration.getObjectFactory();
        this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    }

    /**
     * 返回结果集的处理流程
     * 1.调用结果集包装器处理JDBC返回的数据
     * 2.获取resultMap也就知道了返回对象,将返回对象类传入下一流程中
     * 3.handleResultSet()这个方法后就是一整个流程,实例化对象、找到未被映射的属性,将这些属性反射到对象中
     * 4.将处理过的对象存储到结果处理器,结果上下文中,并返回结果处理器中数据
     * */
    @Override
    public List<Object> handleResultSets(Statement stmt) throws SQLException {
        final List<Object> multipleResults = new ArrayList<>();

        int resultSetCount = 0;
        // 处理返回的列名,类型等数据
        ResultSetWrapper rsw = new ResultSetWrapper(stmt.getResultSet(), configuration);
        // 从mappedStatement获取ResultMap(ResultMap里有id和类型以及ResultMapping)
        List<ResultMap> resultMaps = mappedStatement.getResultMaps();
        while (rsw != null && resultMaps.size() > resultSetCount) {
            ResultMap resultMap = resultMaps.get(resultSetCount);
            // 处理封装保存结果
            handleResultSet(rsw, resultMap, multipleResults, null);
            rsw = getNextResultSet(stmt);
            resultSetCount++;
        }
        return multipleResults.size() == 1 ? (List<Object>) multipleResults.get(0) : multipleResults;
    }

    private ResultSetWrapper getNextResultSet(Statement stmt) {
        try {
            if (stmt.getConnection().getMetaData().supportsMultipleResultSets()) {
                if (!((!stmt.getMoreResults()) && (stmt.getUpdateCount() == -1))) {
                    ResultSet rs = stmt.getResultSet();
                    return rs != null ? new ResultSetWrapper(rs, configuration) : null;
                }
            }
        } catch (Exception ihnore) {

        }
        return null;
    }

    private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
        if (resultHandler == null) {
            // 1.新创建结果处理器
            DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
            // 2.封装结果数据
            handleRowValuesForSimpleResultMap(rsw, resultMap, defaultResultHandler, rowBounds, null);
            // 3.保存结果,从resultHandler拿到上下文结果数据
            multipleResults.add(defaultResultHandler.getResultList());
        }
    }

    private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
        // 实例化默认结果上下文
        DefaultResultContext resultContext = new DefaultResultContext();
        while (resultContext.getResultCount() < rowBounds.getLimit() && rsw.getResultSet().next()) {
            // 为对象赋值
            // 1.实例化对象
            // 2.找到未被映射的属性名称
            // 3.根据属性名称进行一系列反射工具反射设置对象属性值.
            Object rowValue = getRowValue(rsw, resultMap);
            // 存储结果
            callResultHandler(resultHandler, resultContext, rowValue);
        }
    }

    /**
     * 返回结果存储
     * */
    private void callResultHandler(ResultHandler resultHandler, DefaultResultContext resultContext, Object rowValue) throws SQLException {
        // 将返回结果赋值给resultContext
        resultContext.nextResultObject(rowValue);
        // 将结果存储到resultHandler
        resultHandler.handleResult(resultContext);
    }

    private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
        // 根据返回值类型,实例化对象
        Object resultObject = createResultObject(rsw, resultMap, null);
        if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
            final MetaObject metaObject = configuration.newMetaObject(resultObject);
            applyAutomaticMappings(rsw, resultMap, metaObject, null);
        }
        return resultObject;
    }

    private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
        // 得到未被处理映射的列名
        final List<String> unmappedColumnNames = rsw.getUnMappedColumnNamesMap(resultMap, columnPrefix);
        boolean foundValues = false;
        for (String columnName : unmappedColumnNames) {
            String propertyName = columnName;
            if (columnPrefix != null && !columnPrefix.isEmpty()) {
                // When columnPrefix is specified,ignore columns without the prefix.
                if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {
                    propertyName = columnName.substring(columnPrefix.length());
                } else {
                    continue;
                }
            }
            // 从反射工具根据属性名称获取属性
            final String property = metaObject.findProperty(propertyName, false);
            // 这个属性有set方法
            if (property != null && metaObject.hasSetter(property)) {
                // 获取当前set方法属性类型
                final Class<?> propertyType = metaObject.getSetterType(property);
                // 根据属性类型获取对应类型处理器
                if (typeHandlerRegistry.hasTypeHandler(propertyType)) {
                    // 使用 TypeHandler 取得结果
                    final TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
                    // 根据jdbc的数据获取当前属性名称、类型下的数据
                    final Object value = typeHandler.getResult(rsw.getResultSet(), columnName);
                    if (value != null) {
                        foundValues = true;
                    }
                    if (value != null || propertyType.isPrimitive()) {
                        // 通过反射工具类设置属性值,假如返回某个对象,那么这个对象的set方法将被调用
                      metaObject.setValue(property, value);
                    }
                }
            }
        }
        return foundValues;
    }

    // 创建返回值对象
    private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
        // 定义构造方法参数类型
        final List<Class<?>> constructorArgTypes = new ArrayList<>();
        // 定义构造方法参数数据
        final List<Object> constructorArgs = new ArrayList<>();
        return createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
    }

    /**
     * 通过反射工具具体创建返回对象
     */
    private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix) throws SQLException {
        final Class<?> resultType = resultMap.getType();
        final MetaClass metaType = MetaClass.forClass(resultType);
        if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
            // 获取普通的bean对象类型
            return objectFactory.create(resultType);
        }
        throw new RuntimeException("Do not know how to create an instance of " + resultType);
    }

}

4.2.2 结果集包装器

为结果集处理器提供支持,如获取对应类型处理器,数据集属性,未被映射的属性,这样结果集处理器才会将未被映射的属性进行映射。

/**
 * @Author df
 * @Date 2023/3/31 12:31
 * @Version 1.0
 * 结果集包装器
 */
public class ResultSetWrapper {
    // JDBC返回的结果集
    private final ResultSet resultSet;
    // 类型处理注册器,可以获取具体类型
    private final TypeHandlerRegistry typeHandlerRegistry;
    // 属性名称
    private final List<String> columnNames = new ArrayList<>();
    private final List<String> classNames = new ArrayList<>();
    // JDBC类型
    private final List<JdbcType> jdbcTypes = new ArrayList<>();
    private final Map<String, Map<Class<?>, TypeHandler<?>>> typeHandlerMap = new HashMap<>();
    // 已被映射的缓存
    private final Map<String, List<String>> mappedColumnNamesMap = new HashMap<>();
    // 未被映射的列的缓存
    private final Map<String, List<String>> unMappedColumnNamesMap = new HashMap<>();

    public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
        super();
        this.resultSet = rs;
        this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        final ResultSetMetaData metaData = rs.getMetaData();
        final int columnCount = metaData.getColumnCount();
        // 处理属性名称封装,jdbc类型封装,属性类型封装
        for (int i = 1; i <= columnCount; i++) {
            columnNames.add(metaData.getColumnLabel(i));
            jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
            classNames.add(metaData.getColumnClassName(i));
        }
    }

    public ResultSet getResultSet() {
        return resultSet;
    }

    public TypeHandlerRegistry getTypeHandlerRegistry() {
        return typeHandlerRegistry;
    }

    public List<String> getColumnNames() {
        return columnNames;
    }

    public List<String> getClassNames() {
        return classNames;
    }
    
    // 获取类型处理器,在从jdbc结果集中获取值则可以通过对应类型去获取了
    public TypeHandler<?> getTypeHandler(Class<?> propertyType, String columnName) {
        TypeHandler<?> handler = null;
        Map<Class<?>, TypeHandler<?>> columnHandlers = typeHandlerMap.get(columnName);
        if (columnHandlers == null) {
            columnHandlers = new HashMap<>();
            typeHandlerMap.put(columnName, columnHandlers);
        } else {
            handler = columnHandlers.get(propertyType);
        }
        if (handler == null) {
            // 从注册器获取当前类型处理器
            handler = typeHandlerRegistry.getTypeHandler(propertyType, null);
            columnHandlers.put(propertyType, handler);
        }
        return handler;
    }

    public List<JdbcType> getJdbcTypes() {
        return jdbcTypes;
    }

    public Map<String, Map<Class<?>, TypeHandler<?>>> getTypeHandlerMap() {
        return typeHandlerMap;
    }

    public Map<String, List<String>> getMappedColumnNamesMap() {
        return mappedColumnNamesMap;
    }
    
    // 获取未被映射的属性
    public List<String> getUnMappedColumnNamesMap(ResultMap resultMap, String columnPrefix) {
        List<String> unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
        if (unMappedColumnNames == null) {
            // 加载没有被映射的属性名
            loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);
            // 从缓存获取未被映射的属性名数据
            unMappedColumnNames =  unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));
        }
        return unMappedColumnNames;
    }

    /**
     * 加载没有被映射的属性名
     * */
    private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) {
        // 有被映射的属性名
        List<String> mappedColumnNames = new ArrayList<>();
        // 没有被映射的属性名
        List<String> unmappedColumnNames = new ArrayList<>();
        final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);
        // 已被映射的属性名
        final Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);
        for (String columnName : columnNames) {
            final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);
            if (mappedColumns.contains(upperColumnName)) {
                mappedColumnNames.add(upperColumnName);
            } else {
                unmappedColumnNames.add(columnName);
            }
        }
        mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);
        unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);
    }

    private Set<String> prependPrefixes(Set<String> columnNames, String prefix) {
        if (columnNames == null || columnNames.isEmpty() || prefix == null || prefix.length() == 0) {
            return columnNames;
        }
        final Set<String> prefixed = new HashSet<>();
        for (String columnName : columnNames) {
            prefixed.add(prefix + columnName);
        }
        return prefixed;
    }

    private String getMapKey(ResultMap resultMap, String columnPrefix) {
        return resultMap.getId() + ":" + columnPrefix;
    }
}

 4.2.3 类型处理器

上节类型处理器只定义了参数的设置,本节需要添加上定义获取返回值

public interface TypeHandler<T> {
    T getResult(ResultSet rs, String columnName) throws SQLException;
}

   Long类型处理器获取返回值,则从JDBC的结果集中getLong就可以获取相应类型的值

public class LongTypeHandler extends BaseTypeHandler<Long> {
    // 省略..

    @Override
    public Long getResult(ResultSet rs, String columnName) throws SQLException {
        return rs.getLong(columnName);
    }
}

 String类型处理器获取返回值,则从JDBC的结果集中getString就可以获取相应类型的值

public class StringTypeHandler extends BaseTypeHandler<String> {
    // 省略..    

    @Override
    public String getResult(ResultSet rs, String columnName) throws SQLException {
        return rs.getString(columnName);
    }
}

4.2.4 结果上下文

结果上下文接口

/**
 * @Author df
 * @Date 2023/3/30 12:48
 * @Version 1.0
 * 结果上下文
 */
public interface ResultContext {
    /**
     * 获取结果
     */
    Object getResultObject();
    /**
     * 获取记录数
     */
    int getResultCount();
}

实现接口,结果上下文存储结果对象

/**
 * @Author df
 * @Date 2023/3/30 12:52
 * @Version 1.0
 * 默认结果上下文
 */
public class DefaultResultContext implements ResultContext {
    // 结果数据对象
    private Object resultObject;
    private int resultCount;

    public DefaultResultContext() {
        this.resultObject = null;
        this.resultCount = 0;
    }

    @Override
    public Object getResultObject() {
        return resultObject;
    }

    @Override
    public int getResultCount() {
        return resultCount;
    }

    public void nextResultObject(Object resultObject) {
        resultCount++;
        this.resultObject = resultObject;
    }
}

 4.2.5 结果处理器

将上下文结果行数据存储到结果器中。

/**
 * @Author df
 * @Date 2023/3/30 12:44
 * @Version 1.0
 * 默认结果处理器
 */
public class DefaultResultHandler implements ResultHandler {
    // 存储结果上下文数据
    private final List<Object> list;

    public DefaultResultHandler() {
        this.list = new ArrayList<>();
    }
    /**
     * 通过 ObjectFactory 反射工具类,产生特定的空List
       用 objectFactory.create(List.class)创建并没有什么特殊,只是统一工具类
     */
    public DefaultResultHandler(ObjectFactory objectFactory) {
        this.list = objectFactory.create(List.class);
    }

    @Override
    public void handleResult(ResultContext resultContext) {
        list.add(resultContext.getResultObject());
    }
    public List<Object> getResultList() {
        return list;
    }

}

5.测试

配置文件都还是上节的就可以,以下单元测试

public class TestApi {
    private SqlSession sqlSession;

    @Before
    public void init() throws IOException {
        // 1. 从SqlSessionFactory中获取SqlSession
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config-datasource.xml"));
        sqlSession = sqlSessionFactory.openSession();
    }


    @org.junit.Test
    public void test_queryUserInfoById() {
        // 1. 获取映射器对象
        IUserDao userDao = sqlSession.getMapper(IUserDao.class);

        // 2. 测试验证:基本参数
        User user = userDao.queryUserInfoById(1L);
        System.out.println("测试结果:" + user.getUserName());
    }


}

 修改完后的代码依然能够获取返回对象,得到对应的返回值,大家也可以打电话看看每一步的变化。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/428796.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

不通过鲁大师进行硬件检测

文章目录简介dxdiag系统信息设备管理器任务管理器PowerShelldxdiag系统信息设备管理器任务管理器PowerShellTODO&#xff1a;CPU-ZTODO&#xff1a;Everest参考文献简介 不少二手电脑重装系统后发现是假配置&#xff08;特别是XP系统&#xff09;&#xff0c;可以使用系统自带…

基于Vue2实现滚动过程中数据懒加载

以下为实现滚动懒加载的过程&#xff1a; 1、在data对象中定义了items数组&#xff0c;用于存放已加载的item&#xff0c;loading状态&#xff0c;当前页数page&#xff0c;每页数量pageSize&#xff0c;以及距离底部的阈值threshold。 2、在mounted钩子函数中&#xff0c;首次…

Adaptive AUTOSAR——State Management(VRTE 3.0 R21-11)

状态管理是自适应平台服务中的一个功能集群。 在自适应平台中&#xff0c;状态决定了一组活动的自适应应用程序。 特定于项目的应用程序&#xff0c;即状态管理器&#xff0c;决定何时请求状态更改&#xff0c;从而更改当前活动的应用程序集。状态管理器是特定于项目的&#…

【Golang】三分钟让你快速了解Go语言为什么我们需要Go语言?

博主简介&#xff1a;努力学习的大一在校计算机专业学生&#xff0c;热爱学习和创作。目前在学习和分享&#xff1a;数据结构、Go&#xff0c;Java等相关知识。博主主页&#xff1a; 是瑶瑶子啦所属专栏: Go语言核心编程近期目标&#xff1a;写好专栏的每一篇文章 目录一、Go语…

Pytorch基础 - 3. torch.utils.tensorboard

目录 1. 简介 2. 基本步骤 3. 示例1 - 可视化单条曲线 4. 示例2 - 可视化多条曲线 5. 示例3 - 可视化网络结构 1. 简介 Tensorboard是Tensorflow的可视化工具&#xff0c;常用来可视化网络的损失函数&#xff0c;网络结构&#xff0c;图像等。后来将Tensorboard集成到了P…

wps如何修改已经存在的目录标题内容?

如需了解更多办公应用的相关知识&#xff0c;可进入到赛效官网查看应用资讯或者应用问答栏目。 在WPS文档中&#xff0c;为方便大家查看文档中的内容&#xff0c;可以给文档内容添加目录&#xff0c;很多人由于对文档功能不太熟悉&#xff0c;所以当目录生成以后&#xff0c;想…

高效又稳定的ChatGPT大模型训练技巧总结,让训练事半功倍!

文&#xff5c;python前言近期&#xff0c;ChatGPT成为了全网热议的话题。ChatGPT是一种基于大规模语言模型技术&#xff08;LLM&#xff0c; large language model&#xff09;实现的人机对话工具。现在主流的大规模语言模型都采用Transformer网络&#xff0c;通过极大规模的数…

Day941.仓库版本管理 -系统重构实战

仓库&版本管理 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于仓库&版本管理的内容。 当代码以及团队达到一定的规模以后&#xff0c;这会给项目仓库和版本的管理带来诸多问题。 一张常见问题的表格&#xff0c;不妨“对号入座”&#xff0c;看看项目有没…

【常见CSS扫盲雪碧图】从源码细看CSS雪碧图原理及实现,千字详解【附源码demo下载】

【写在前面】其实估计很多人都听过雪碧图&#xff0c;或者是CSS-Sprite&#xff0c;在很多门户网站就会经常有用到的&#xff0c;之所有引出雪碧图这个概念还得从前端加载多个图片时候页面闪了一下说起&#xff0c;这样给人的视觉效果体验很差&#xff0c;也就借此机会和大家说…

Linux查看串行端口

查看串口驱动 cat /proc/tty/driver/serial查看串口设备 dmesg | grep ttyS*[rootlocalhost driver]# cat /proc/tty/driver/serial serinfo:1.0 driver revision: 0: uart:16550A port:000003F8 irq:4 tx:0 rx:0 1: uart:16550A port:000002F8 irq:3 tx:0 rx:0 CTS|DSR|CD 2:…

攻防世界-inget(简单的SQL注入、万能密码)

目录 1、手工注入&#xff08;万能密码原理&#xff09; 2、sqlmap实现 3、常用参数 1、手工注入&#xff08;万能密码原理&#xff09; 打开链接&#xff0c;提示输入id并尝试绕过 尝试一下万能密码&#xff0c;使用逻辑语句使结果返回 构造payload /?id or 我们这里是…

Java虚拟机内存区域

Java虚拟机所管理的内存将会包括以下几个运行时数据区域 程序计数器 是一块较小的内存空间&#xff0c;可以看作当前线程所执行的字节码的行号指示器。分支、循环、跳转、异常处理、线程恢复等基础功能都需要通过更改这个计数器的值来改变下一条需要执行的字节码。 由于各个线…

DAY 35 sed文本编辑器

文本三剑客&#xff1a;都是按行读取后处理。 grep 过滤行内容 awk 过滤字段 sed 过滤行内容&#xff1b;修改行内容 sed编辑器 sed是一种流编辑器&#xff0c;流编辑器会在编辑器处理数据之前基于预先提供的一组规则来编辑数据流 sed编辑器可以根据命令来处理数据流中的…

Shell编程(二)

上一章&#xff1a; Shell编程_做测试的喵酱的博客-CSDN博客 七、ping shell脚本&#xff0c;是一个面向过程的编程语言&#xff0c;没有类和对象的概念。用的最多的就是函数。 查看当前服务&#xff0c;是否能ping通baidu ping www.baidu.com [rootecs-39233 dev]# ping ww…

眺望2023:房企“三大护法”如何助力穿越周期?

2022 年的地产行业尤为艰难&#xff0c;土地和商品房成交量明显下滑&#xff0c;许多房企的业绩都呈现收缩态势。 不过&#xff0c;今年以来房地产市场似乎出现企稳信号。国家统计局数据显示&#xff0c;今年1~2月全国商品房销售面积和销售额的下跌幅度明显收窄&#xff0c;住…

18-java单列集合

java单列集合1.集合体系结构1.1 单列集合1.2 双列集合2. 单列集合体系结构3.Collection集合顶层接口3.1 Collection的遍历方法3.1.1 迭代器遍历3.1.2 增强for遍历3.1.3 Lambda表达式4. List单列集合4.1 List4.1.1 List集合特点4.1.2 List集合的特有方法4.1.3 List集合遍历方法4…

计算机组成原理实验三-----系统总线和具有基本输入输出功能的总线接口实验

总线是计算机中连接各个功能部件的纽带&#xff0c;是计算机各部件之间进行信息传输的公共通路。 总线不只是一组简单的信号传输线&#xff0c;它还是一组协议。他有两大特征 分时: 同一总线在同一时刻&#xff0c;只能有一个部件占领总线发送信息&#xff0c;其他部件要发送信…

游戏基础—Android平台进程模块信息获取

记得学习编程时的第一个helloworld程序&#xff1a; #include<stdio.h> Int main(int argc, char **argv) { printf(“Hello World”); return 0; } 打印” Hello World”&#xff0c;使用的是printf函数。但是&#xff0c;我们并没有去实现printf函数的功能&#xff0…

javascrip语法

JavaScript 语法涉及到很多方面&#xff0c;以下是常见的详细语法&#xff1a; 1. 变量 变量用于存放值&#xff0c;关键字 var 可以定义变量。 // javascript var x 5; // 定义变量 x&#xff0c;并赋值为 52. 数据类型 JavaScript 中有多种数据类型&#xff0c;包括&…

iOS 紧急通知

一般通知 关于通知的各种配置和开发&#xff0c;可以参考推送通知教程&#xff1a;入门 – Kodeco&#xff0c;具有详细步骤。 紧急通知表现 紧急通知不受免打扰模式和静音模式约束。当紧急通知到达时&#xff0c;会有短暂提示音量和抖动&#xff08;约2s&#xff09;。未锁…