手写Mybatis:第14章-解析和使用ResultMap映射参数配置

news2024/11/25 5:23:48

文章目录

  • 一、目标:ResultMap映射参数
  • 二、设计:ResultMap映射参数
  • 三、实现:ResultMap映射参数
    • 3.1 工程结构
    • 3.2 ResultMap映射参数类图
    • 3.3 添加类型处理器
      • 3.3.1 日期类型处理器
      • 3.3.2 类型处理器注册机
    • 3.4 存放映射对象
      • 3.4.1 结果标志
      • 3.4.2 结果映射Map
      • 3.4.3 封装结果映射
      • 3.4.4 解析结果映射
    • 3.5 解析映射配置
      • 3.5.1 构建器基类
      • 3.5.2 映射构建器助手
      • 3.5.3 解析映射配置
    • 3.6 使用映射对象
  • 四、测试:ResultMap映射参数
    • 4.1 测试环境配置
      • 4.1.1 在mybatis数据库添加activity数据表
      • 4.1.2 提供DAO接口和实体类
      • 4.1.3 配置数据源和配置Mapper
      • 4.1.4 活动接口配置文件
    • 4.2 单元测试
  • 五、总结:ResultMap映射参数

一、目标:ResultMap映射参数

💡 如何将数据库表中的下划线的字段名称,映射成Java代码中的驼峰字段?

  • 通常在数据库表字段的命名中,所定义的规范是希望使用小写的英文字母和下划线的方式组合使用。
    • 例如:雇员表中的雇员姓名,则使用 employee_name 表字段描述。
  • 但这样的字段定义与 Java 代码开发中的 PO 数据库对象中的字段,是不能一一匹配的。因为 Java 代码中会使用驼峰的方式进行命名。
    • 同样是雇员姓名在 Java 代码中则是 employeeName 的方式进行表示。
  • 在使用 Mybatis 框架的时候,如果遇到这样的字段,则需要通过把数据库表中的下划线的字段名称,映射成 Java 代码中的驼峰字段,这样才能在执行查询操作的时候,正确的把数据库中的结果映射到 Java 代码的返回对象上。
  • 注意Mybatis 中也可以使用例如 employee_name as employeeName 的方式进行处理,但在整个编程中并不是太优雅。
    • 因为所有的查询都要做 as 映射,那么使用一个统一的字段映射更加合理。

二、设计:ResultMap映射参数

💡 用一个标准的结构适配非映射类的对象属性。

  • 之前处理解析 Mapper XML 中的 select 语句下配置的 resultType 时,其实就已经添加了 ResultMap、ResultMapping 的映射结构。
  • 不过之前对于返回类型的处理都直接是对象类型,没有使用映射参数。而这也就代表着,在处理查询结果集后,SQL 对应的查询字段与 Java 代码中类的属性字段是一一对应的,所以不要使用映射,直接按照匹配的属性名称设置值即可。
  • 之所以采用这样通用的结果类型包装结构,是为了做统一的方式处理,也相当于是在定义标准,用一个标准的结构适配非映射类的对象属性。
  • 可以借助开发好的 ResultMap 封装参数结构,完善对字段映射的处理。

在这里插入图片描述

  • 完善 ResultMapping 结果映射中 Builder 构建者的对映射属性的保存操作,并使用到解析 Mapper XML 中对 resultMap 元素的处理。
  • 映射参数的解析过程:主要以循环解析 resultMap 的标签集合,摘取核心的 property、column 字段构建出 ResultMapping 结果映射类。
    • 每一条配置都会创建出一个 ResultMapping 类。
    • 最后这个配置信息会被写入到 Configuration 配置项的 Map<String, ResultMap> resultMaps 的结果映射中。
  • 最后在程序执行获取到 Mapper 一直调用到 DefaultSqlSession 查询结果封装时,再从配置项中把相关的 ResultMap 读取出来,进行设置属性值。

三、实现:ResultMap映射参数

3.1 工程结构

mybatis-step-13
|-src
	|-main
	|	|-java
	|		|-com.lino.mybatis
    |			|-annotations
    |			|	|-Delete.java
	|			|	|-Insert.java
	|			|	|-Select.java
    |			|	|-Update.java
    |			|-binding
    |			|	|-MapperMethod.java
	|			|	|-MapperProxy.java
	|			|	|-MapperProxyFactory.java
    |			|	|-MapperRegistry.java
    |			|-builder
    |			|	|-annotations
    |			|	|	|-MapperAnnotationBuilder.java
    |			|	|-xml
    |			|	|	|-XMLConfigBuilder.java
    |			|	|	|-XMLMapperBuilder.java
    |			|	|	|-XMLStatementBuilder.java
    |			|	|-BaseBuilder.java
    |			|	|-MapperBuilderAssistant.java
    |			|	|-ParameterExpression.java
    |			|	|-ResultMapResolver.java
    |			|	|-SqlSourceBuilder.java
    |			|	|-StaticSqlSource.java
	|			|-datasource
	|			|	|-druid
	|			|	|	|-DruidDataSourceFacroty.java
	|			|	|-pooled
	|			|	|	|-PooledConnection.java
	|			|	|	|-PooledDataSource.java
	|			|	|	|-PooledDataSourceFacroty.java
	|			|	|	|-PoolState.java
	|			|	|-unpooled
	|			|	|	|-UnpooledDataSource.java
	|			|	|	|-UnpooledDataSourceFacroty.java
	|			|	|-DataSourceFactory.java
	|			|-executor
	|			|	|-parameter
	|			|	|	|-ParameterHandler.java
	|			|	|-result
	|			|	|	|-DefaultResultContext.java
	|			|	|	|-DefaultResultHandler.java
	|			|	|-resultset
	|			|	|	|-DefaultResultSetHandler.java
	|			|	|	|-ResultSetHandler.java
	|			|	|	|-ResultSetWrapper.java
	|			|	|-statement
	|			|	|	|-BaseStatementHandler.java
	|			|	|	|-PreparedStatementHandler.java
	|			|	|	|-SimpleStatementHandler.java
	|			|	|	|-StatementHandler.java
	|			|	|-BaseExecutor.java
	|			|	|-Executor.java
	|			|	|-SimpleExecutor.java
    |			|-io
    |			|	|-Resources.java
    |			|-mapping
    |			|	|-BoundSql.java
    |			|	|-Environment.java
    |			|	|-MappedStatement.java
    |			|	|-ParameterMapping.java
    |			|	|-ResultFlag.java
    |			|	|-ResultMap.java
    |			|	|-ResultMapping.java
    |			|	|-SqlCommandType.java
    |			|	|-SqlSource.java
    |			|-parsing
    |			|	|-GenericTokenParser.java
    |			|	|-TokenHandler.java
	|			|-reflection
	|			|	|-factory
	|			|	|	|-DefaultObjectFactory.java
	|			|	|	|-ObjectFactory.java
	|			|	|-invoker
	|			|	|	|-GetFieldInvoker.java
	|			|	|	|-Invoker.java
	|			|	|	|-MethodInvoker.java
	|			|	|	|-SetFieldInvoker.java
	|			|	|-property
	|			|	|	|-PropertyNamer.java
	|			|	|	|-PropertyTokenizer.java
	|			|	|-wrapper
	|			|	|	|-BaseWrapper.java
	|			|	|	|-BeanWrapper.java
	|			|	|	|-CollectionWrapper.java
	|			|	|	|-DefaultObjectWrapperFactory.java
	|			|	|	|-MapWrapper.java
	|			|	|	|-ObjectWrapper.java
	|			|	|	|-ObjectWrapperFactory.java
	|			|	|-MetaClass.java
	|			|	|-MetaObject.java
	|			|	|-Reflector.java
	|			|	|-SystemMetaObject.java
	|			|-scripting
	|			|	|-defaults
	|			|	|	|-DefaultParameterHandler.java
	|			|	|	|-RawSqlSource.java
	|			|	|-xmltags
	|			|	|	|-DynamicContext.java
	|			|	|	|-MixedSqlNode.java
	|			|	|	|-SqlNode.java
	|			|	|	|-StaticTextSqlNode.java
	|			|	|	|-XMLLanguageDriver.java
	|			|	|	|-XMLScriptBuilder.java
	|			|	|-LanguageDriver.java
	|			|	|-LanguageDriverRegistry.java
    |			|-session
    |			|	|-defaults
    |			|	|	|-DefaultSqlSession.java
    |			|	|	|-DefaultSqlSessionFactory.java
    |			|	|-Configuration.java
    |			|	|-ResultContext.java
    |			|	|-ResultHandler.java
    |			|	|-RowBounds.java
    |			|	|-SqlSession.java
    |			|	|-SqlSessionFactory.java
    |			|	|-SqlSessionFactoryBuilder.java
    |			|	|-TransactionIsolationLevel.java
    |			|-transaction
    |			|	|-jdbc
    |			|	|	|-JdbcTransaction.java
    |			|	|	|-JdbcTransactionFactory.java
    |			|	|-Transaction.java
    |			|	|-TransactionFactory.java
    |			|-type
    |			|	|-BaseTypeHandler.java
    |			|	|-DateTypeHandler.java
    |			|	|-IntegerTypeHandler.java
    |			|	|-JdbcType.java
    |			|	|-LongTypeHandler.java
    |			|	|-StringTypeHandler.java
    |			|	|-TypeAliasRegistry.java
    |			|	|-TypeHandler.java
    |			|	|-TypeHandlerRegistry.java
	|-test
		|-java
		|	|-com.lino.mybatis.test
		|	|-dao
		|	|	|-IActivityDao.java
		|	|-po
		|	|	|-Activity.java
		|	|-ApiTest.java
        |-resources
			|-mapper
			|	|-Activity_Mapper.xml
        	|-mybatis-config-datasource.xml

3.2 ResultMap映射参数类图

在这里插入图片描述

  • XMLMapperBuilder 解析为入口,扩展 resultMapElements 方法,解析 resultMap 映射参数。
    • 解析过程涉及到 MapperBuilderAssistant 映射器构建助手类的使用,所以需要在 XMLMapperBuilder 构建函数中进行初始化。
    • 参数的解析细节主要在 MapperBuilderAssistant 映射构建器助手中完成。
      • 包括解析 javaTypeClass、typeHandlerInstance,以及封装 XML 配置的基本字段映射信息。
  • 一个 DAO 方法的执行,需要从 Mapper 映射器获取开始,并逐步拿到映射器代理、映射器方法和 DefaultSqlSession 的执行。
  • 最终在执行后封装返回结果时,就可以按照我们已经提供好的结果映射参数进行处理。
    • 这部分操作也就是 DefaultResultSetHandler#applyPropertyMappings 的处理过程。

3.3 添加类型处理器

3.3.1 日期类型处理器

DateTypeHandler.java

package com.lino.mybatis.type;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Date;

/**
 * @description: 日期类型处理器
 * @author: lingjian
 * @createDate: 2022/11/11 14:01
 */
public class DateTypeHandler extends BaseTypeHandler<Date> {
    @Override
    protected void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {
        ps.setTimestamp(i, new Timestamp((parameter).getTime()));
    }

    @Override
    protected Date getNullableResult(ResultSet rs, String columnName) throws SQLException {
        Timestamp sqlTimestamp = rs.getTimestamp(columnName);
        if (sqlTimestamp != null) {
            return new Date(sqlTimestamp.getTime());
        }
        return null;
    }
}

3.3.2 类型处理器注册机

TypeHandlerRegistry.java

package com.lino.mybatis.type;

import java.lang.reflect.Type;
import java.util.Date;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;

/**
 * @description: 类型处理器注册机
 */
public final class TypeHandlerRegistry {

    private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<>(JdbcType.class);
    private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap<>(16);
    private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLER_MAP = new HashMap<>(16);

    public TypeHandlerRegistry() {
        register(Long.class, new LongTypeHandler());
        register(long.class, new LongTypeHandler());

        register(Integer.class, new IntegerTypeHandler());
        register(int.class, new IntegerTypeHandler());

        register(String.class, new StringTypeHandler());
        register(String.class, JdbcType.CHAR, new StringTypeHandler());
        register(String.class, JdbcType.VARCHAR, new StringTypeHandler());

        register(Date.class, new DateTypeHandler());
    }

    private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
        register(javaType, null, typeHandler);
    }

    private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
        if (null != javaType) {
            Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.computeIfAbsent(javaType, k -> new HashMap<>(16));
            map.put(jdbcType, handler);
        }
        ALL_TYPE_HANDLER_MAP.put(handler.getClass(), handler);
    }

    @SuppressWarnings("unchecked")
    public TypeHandler<?> getTypeHandler(Class<?> type, JdbcType jdbcType) {
        return getTypeHandler((Type) type, jdbcType);
    }

    public boolean hasTypeHandler(Class<?> javaType) {
        return hasTypeHandler(javaType, null);
    }

    public boolean hasTypeHandler(Class<?> javaType, JdbcType jdbcType) {
        return javaType != null && getTypeHandler((Type) javaType, jdbcType) != null;
    }

    private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
        Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
        TypeHandler<?> handler = null;
        if (jdbcHandlerMap != null) {
            handler = jdbcHandlerMap.get(jdbcType);
            if (handler == null) {
                handler = jdbcHandlerMap.get(null);
            }
        }
        // type driver generics here
        return (TypeHandler<T>) handler;
    }

    public TypeHandler<?> getMappingTypeHandler(Class<? extends TypeHandler<?>> handlerType) {
        return ALL_TYPE_HANDLER_MAP.get(handlerType);
    }
}

3.4 存放映射对象

3.4.1 结果标志

ResultFlag.java

package com.lino.mybatis.mapping;

/**
 * @description: 结果标志
 */
public enum ResultFlag {
    /**
     * 结果标志
     */
    ID, CONSTRUCTOR
}

3.4.2 结果映射Map

ResultMapping.java

package com.lino.mybatis.mapping;

import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.type.JdbcType;
import com.lino.mybatis.type.TypeHandler;
import com.lino.mybatis.type.TypeHandlerRegistry;
import java.util.ArrayList;
import java.util.List;

/**
 * @description: 结果映射Map
 */
public class ResultMapping {

    private Configuration configuration;
    private String property;
    private String column;
    private Class<?> javaType;
    private TypeHandler<?> typeHandler;
    private List<ResultFlag> flags;

    public ResultMapping() {
    }

    public static class Builder {

        private ResultMapping resultMapping = new ResultMapping();

        public Builder(Configuration configuration, String property, String column, Class<?> javaType) {
            resultMapping.configuration = configuration;
            resultMapping.property = property;
            resultMapping.column = column;
            resultMapping.javaType = javaType;
            resultMapping.flags = new ArrayList<>();
        }

        public Builder typeHandler(TypeHandler<?> typeHandler) {
            resultMapping.typeHandler = typeHandler;
            return this;
        }

        public Builder flags(List<ResultFlag> flags) {
            resultMapping.flags = flags;
            return this;
        }

        public ResultMapping build() {
            resolveTypeHandler();
            return resultMapping;
        }

        private void resolveTypeHandler() {
            if (resultMapping.typeHandler == null && resultMapping.javaType != null) {
                Configuration configuration = resultMapping.configuration;
                TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
                resultMapping.typeHandler = typeHandlerRegistry.getTypeHandler(resultMapping.javaType, null);
            }
        }
    }

    public Configuration getConfiguration() {
        return configuration;
    }

    public String getProperty() {
        return property;
    }

    public String getColumn() {
        return column;
    }

    public Class<?> getJavaType() {
        return javaType;
    }

    public TypeHandler<?> getTypeHandler() {
        return typeHandler;
    }

    public List<ResultFlag> getFlags() {
        return flags;
    }
}

3.4.3 封装结果映射

  • ResultMap 映射对象的封装主要包括了对象的构建和结果的存放。
  • 存放的地点就是 Configuration 配置项中所提供的结果映射 Map<String, ResultMap> resultMaps
  • 这样的配置方式也是为了后续可以通过 resultMaps Key 获取到对应的 ResultMap 进行使用。

ResultMap.java

package com.lino.mybatis.mapping;

import com.lino.mybatis.session.Configuration;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

/**
 * @description: 结果映射
 */
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<>();
            // 添加 mappedColums 字段
            for (ResultMapping resultMapping : resultMap.resultMappings) {
                final String column = resultMapping.getColumn();
                if (column != null) {
                    resultMap.mappedColumns.add(column.toUpperCase(Locale.ENGLISH));
                }
            }
            return resultMap;
        }
    }

    public String getId() {
        return id;
    }

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

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

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

    public List<ResultMapping> getPropertyResultMappings() {
        return resultMappings;
    }
}
  • ResultMapBuilder 建造者负责完成字段的处理,通过把字段统一转换为大写存放到 mappedColumns 映射字段中。并返回 resultMap 对象。
  • 其余的信息都可以通过构造函数进行传递。

3.4.4 解析结果映射

ResultMapResolver.java

package com.lino.mybatis.builder;

import com.lino.mybatis.mapping.ResultMap;
import com.lino.mybatis.mapping.ResultMapping;
import java.util.List;

/**
 * @description: 结果映射解析器
 */
public class ResultMapResolver {

    private final MapperBuilderAssistant assistant;
    private String id;
    private Class<?> type;
    private List<ResultMapping> resultMappings;

    public ResultMapResolver(MapperBuilderAssistant assistant, String id, Class<?> type, List<ResultMapping> resultMappings) {
        this.assistant = assistant;
        this.id = id;
        this.type = type;
        this.resultMappings = resultMappings;
    }

    public ResultMap resolve() {
        return assistant.addResultMap(this.id, this.type, this.resultMappings);
    }
}
  • 新增 ResultMapResolver 结果映射器,它的作用就是对解析结果内容的一个封装处理。
  • 最终调用的还是 MapperBuilderAssistant 映射构建器助手,所提供 ResultMap 封装和保存操作。

3.5 解析映射配置

  • 整个配置解析都以围绕 Mybatis 框架中使用 resultMap 映射为主。
  • resultMap 的参数映射配置也是用于解决数据库表中的字段与 Java 代码中的对象字段不一致的情况。

在这里插入图片描述

3.5.1 构建器基类

BaseBuilder.java

package com.lino.mybatis.builder;

import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.type.TypeAliasRegistry;
import com.lino.mybatis.type.TypeHandler;
import com.lino.mybatis.type.TypeHandlerRegistry;

/**
 * @description: 构建器的基类,建造者模式
 */
public class BaseBuilder {

    protected final Configuration configuration;
    protected final TypeAliasRegistry typeAliasRegistry;
    protected final TypeHandlerRegistry typeHandlerRegistry;

    public BaseBuilder(Configuration configuration) {
        this.configuration = configuration;
        this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
        this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
    }

    public Configuration getConfiguration() {
        return configuration;
    }

    protected Class<?> resolveAlias(String alias) {
        return typeAliasRegistry.resolveAlias(alias);
    }

    /**
     * 根据别名解析 Class 类型别名注册/事务管理器别名
     *
     * @param alias 别名
     * @return 对象类型
     */
    protected Class<?> resolveClass(String alias) {
        if (alias == null) {
            return null;
        }
        try {
            return resolveAlias(alias);
        } catch (Exception e) {
            throw new RuntimeException("Error resolving class. Cause: " + e, e);
        }
    }

    protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, Class<? extends TypeHandler<?>> typeHandlerType) {
        if (typeHandlerType == null) {
            return null;
        }
        return typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);
    }
}

3.5.2 映射构建器助手

MapperBuilderAssistant.java

package com.lino.mybatis.builder;

import com.lino.mybatis.mapping.*;
import com.lino.mybatis.reflection.MetaClass;
import com.lino.mybatis.scripting.LanguageDriver;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.type.TypeHandler;
import java.util.ArrayList;
import java.util.List;

/**
 * @description: 映射构建器助手,建造者
 */
public class MapperBuilderAssistant extends BaseBuilder {

    private String currentNamespace;
    private String resource;

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

    public ResultMapping buildResultMapping(Class<?> resultType, String property, String column, List<ResultFlag> flags) {
        Class<?> javaTypeClass = resolveResultJavaType(resultType, property, null);
        TypeHandler<?> typeHandlerInstance = resolveTypeHandler(javaTypeClass, null);

        ResultMapping.Builder builder = new ResultMapping.Builder(configuration, property, column, javaTypeClass);
        builder.typeHandler(typeHandlerInstance);
        builder.flags(flags);

        return builder.build();
    }

    private Class<?> resolveResultJavaType(Class<?> resultType, String property, Class<?> javaType) {
        if (javaType == null && property != null) {
            try {
                MetaClass metaResultType = MetaClass.forClass(resultType);
                javaType = metaResultType.getSetterType(property);
            } catch (Exception ignore) {

            }
        }
        if (javaType == null) {
            javaType = Object.class;
        }
        return javaType;
    }

    public String getCurrentNamespace() {
        return currentNamespace;
    }

    public void setCurrentNamespace(String currentNamespace) {
        this.currentNamespace = currentNamespace;
    }

    public String applyCurrentNamespace(String base, boolean isReference) {
        if (base == null) {
            return null;
        }
        if (isReference) {
            if (base.contains(".")) {
                return base;
            }
        } else {
            if (base.startsWith(currentNamespace + ".")) {
                return base;
            }
            if (base.contains(".")) {
                throw new RuntimeException("Dots are not allowed in element names, please remove it from " + base);
            }
        }
        return currentNamespace + "." + base;
    }

    /**
     * 添加映射器语句
     */
    public MappedStatement addMappedStatement(String id, SqlSource sqlSource, SqlCommandType sqlCommandType,
                                              Class<?> parameterType, String resultMap, Class<?> resultType,
                                              LanguageDriver lang) {
        // 给id加上namespace前缀:com.lino.mybatis.test.dao.IUserDao.queryUserInfoById
        id = applyCurrentNamespace(id, false);
        MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlCommandType, sqlSource, resultType);

        // 结果映射, 给 MappedStatement#resultMaps
        setStatementResultMap(resultMap, resultType, statementBuilder);

        MappedStatement statement = statementBuilder.build();
        // 映射语句信息,建造完存放到配置项中
        configuration.addMappedStatement(statement);

        return statement;
    }

    private void setStatementResultMap(String resultMap, Class<?> resultType, MappedStatement.Builder statementBuilder) {
        // 因为暂时还没有在 Mapper XML 中配置 Map 返回结果,所以这里返回的是 null
        resultMap = applyCurrentNamespace(resultMap, true);

        List<ResultMap> resultMaps = new ArrayList<>();

        if (resultMap != null) {
            String[] resultMapNames = resultMap.split(",");
            for (String resultMapName : resultMapNames) {
                resultMaps.add(configuration.getResultMap(resultMapName.trim()));
            }
        }
        /*
         * 通常使用 resultType 即可满足大部分场景
         * <select id="queryUserInfoById" resultType="com.lino.mybatis.test.po.User">
         * 使用 resultType 的情况下,Mybatis 会自动创建一个 ResultMap,基于属性名称映射列到 JavaBean 的属性上。
         */
        else if (resultType != null) {
            ResultMap.Builder inlineResultMapBuilder = new ResultMap.Builder(
                    configuration, statementBuilder.id() + "-Inline", resultType, new ArrayList<>());
            resultMaps.add(inlineResultMapBuilder.build());
        }
        statementBuilder.resultMaps(resultMaps);
    }

    public ResultMap addResultMap(String id, Class<?> type, List<ResultMapping> resultMappings) {
        // 补全ID全路径,如:com.lino.mybatis.test.dao.IActivityDao + activityMap
        id = applyCurrentNamespace(id, false);

        ResultMap.Builder inlineResultMapBuilder = new ResultMap.Builder(configuration, id, type, resultMappings);

        ResultMap resultMap = inlineResultMapBuilder.build();
        configuration.addResultMap(resultMap);
        return resultMap;
    }
}
  • MapperBuilderAssistant 映射构建器助手中,新增加了2个方法。
    • 构建 Mapping 方法 buildResultMapping
      • 在最开始 Mapper XML 映射构建器解析 buildResultMappingFromContext 所调用的 XMLMapperBuilder#buildResultMapping 方法。
      • 封装映射配置中 <result column="activity_id" property="activityId" />column、property 字段。
    • 添加 ResultMap 方法 addResultMap
      • ResultMapResolver 结果映射器调用添加 ResultMap
      • 最终就是把这个配置保存到 Configuration 配置项中。

3.5.3 解析映射配置

  • 基于这样对映射字段的解决方案,所以需要扩展 Mapper XML 映射构建器 configurationElement 方法的处理内容。
  • 添加解析 resultMap 操作。这部分操作也就是在解析整个 select、insert、update、delete 部分。

XMLMapperBuilder.java

package com.lino.mybatis.builder.xml;

import com.lino.mybatis.builder.BaseBuilder;
import com.lino.mybatis.builder.MapperBuilderAssistant;
import com.lino.mybatis.builder.ResultMapResolver;
import com.lino.mybatis.io.Resources;
import com.lino.mybatis.mapping.ResultFlag;
import com.lino.mybatis.mapping.ResultMap;
import com.lino.mybatis.mapping.ResultMapping;
import com.lino.mybatis.session.Configuration;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @description: XML映射构建器
 */
public class XMLMapperBuilder extends BaseBuilder {

    private Element element;
    private String resource;
    /**
     * 映射器构建助手
     */
    private MapperBuilderAssistant builderAssistant;

    public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource) throws DocumentException {
        this(new SAXReader().read(inputStream), configuration, resource);
    }

    public XMLMapperBuilder(Document document, Configuration configuration, String resource) {
        super(configuration);
        this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
        this.element = document.getRootElement();
        this.resource = resource;
    }

    /**
     * 解析
     *
     * @throws Exception 异常
     */
    public void parse() throws Exception {
        // 如果当前资源没有加载过再加载,防止重复加载
        if (!configuration.isResourceLoaded(resource)) {
            configurationElement(element);
            // 标记一下,已经加载过了
            configuration.addLoadedResource(resource);
            // 绑定映射器到namespace
            configuration.addMapper(Resources.classForName(builderAssistant.getCurrentNamespace()));
        }
    }

    /**
     * 配置mapper元素
     * <mapper namespace="org.mybatis.example.BlogMapper">
     * <select id="selectBlog" parameterType="int" resultType="Blog">
     * select * from Blog where id = #{id}
     * </select>
     * </mapper>
     *
     * @param element 元素
     */
    private void configurationElement(Element element) {
        // 1.配置namespace
        String namespace = element.attributeValue("namespace");
        if ("".equals(namespace)) {
            throw new RuntimeException("Mapper's namespace cannot be empty");
        }
        builderAssistant.setCurrentNamespace(namespace);

        // 2.解析resultMap
        resultMapElement(element.elements("resultMap"));

        // 3.配置select|insert|update|delete
        buildStatementFromContext(element.elements("select"), element.elements("insert"), element.elements("update"), element.elements("delete"));
    }

    /**
     * 解析resultMap
     *
     * @param list 结果映射列表
     */
    private void resultMapElement(List<Element> list) {
        for (Element element : list) {
            try {
                resultMapElement(element, Collections.emptyList());
            } catch (Exception ignore) {

            }
        }
    }

    /**
     * <resultMap id="activityMap" type="cn.bugstack.mybatis.test.po.Activity">
     * <id column="id" property="id"/>
     * <result column="activity_id" property="activityId"/>
     * <result column="activity_name" property="activityName"/>
     * <result column="activity_desc" property="activityDesc"/>
     * <result column="create_time" property="createTime"/>
     * <result column="update_time" property="updateTime"/>
     * </resultMap>
     */
    private ResultMap resultMapElement(Element resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
        String id = resultMapNode.attributeValue("id");
        String type = resultMapNode.attributeValue("type");
        Class<?> typeClass = resolveClass(type);

        List<ResultMapping> resultMappings = new ArrayList<>();
        resultMappings.addAll(additionalResultMappings);

        List<Element> resultChildren = resultMapNode.elements();
        for (Element resultChild : resultChildren) {
            List<ResultFlag> flags = new ArrayList<>();
            if ("id".equals(resultChild.getName())) {
                flags.add(ResultFlag.ID);
            }
            // 构建 ResultMapping
            resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
        }

        // 创建结果映射解析器
        ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, resultMappings);
        return resultMapResolver.resolve();
    }

    /**
     * <id column="id" property="id"/>
     * <result column="activity_id" property="activityId"/>
     */
    private ResultMapping buildResultMappingFromContext(Element context, Class<?> resultType, List<ResultFlag> flags) throws Exception {
        String property = context.attributeValue("property");
        String column = context.attributeValue("column");
        return builderAssistant.buildResultMapping(resultType, property, column, flags);
    }

    /**
     * 配置select|insert|update|delete
     *
     * @param lists 元素列表
     */
    @SafeVarargs
    private final void buildStatementFromContext(List<Element>... lists) {
        for (List<Element> list : lists) {
            for (Element element : list) {
                final XMLStatementBuilder statementBuilder = new XMLStatementBuilder(configuration, builderAssistant, element);
                statementBuilder.parseStatementNode();
            }
        }
    }
}
  • XMLMapperBuilder#configurationElement 配置元素解析的方法中,新增加了关于 resultMap 元素的解析。
    • 由于可能在一个 Mapper XML 中有多组这样的映射参数配置,所以这里获取的是一个 elements 集合元素。
  • 解析的核心过程包括:
    • 读取 resultMap 标签中,如 <resultMap id="activityMap" type="com.lino.mybatis.test.po.Activity">id、type 信息。
    • 之后循环解析标签内的每条配置元素,如 <result column="activity_id" property="activityId"/> 中的 column、property 信息。
    • 同时会把 id 的配置专门用 ResultFlag 枚举类进行标记。
    • 基础信息解析完成后,就开始调用结果映射器把解析的信息封装成 ResultMap 进行存放。

3.6 使用映射对象

  • DefaultSqlSession 调用方法,执行 SQL 后,就是对结果的封装。
    • 主要体现在 DefaultResultSetHandler#handlerResultSets 结果收集器的操作中。

DefaultResultSetHandler.java

package com.lino.mybatis.executor.resultset;

import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.result.DefaultResultContext;
import com.lino.mybatis.executor.result.DefaultResultHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.mapping.ResultMap;
import com.lino.mybatis.mapping.ResultMapping;
import com.lino.mybatis.reflection.MetaClass;
import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.reflection.factory.ObjectFactory;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.ResultHandler;
import com.lino.mybatis.session.RowBounds;
import com.lino.mybatis.type.TypeHandler;
import com.lino.mybatis.type.TypeHandlerRegistry;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
 * @description: 默认Map结果处理器
 * @author: lingjian
 * @createDate: 2022/11/8 13:59
 */
public class DefaultResultSetHandler implements ResultSetHandler {

    private static final Object NO_VALUE = new Object();

    ...

    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);
            // Map映射:根据映射类型赋值到字段
            applyPropertyMappings(rsw, resultMap, metaObject, null);
        }
        return resultObject;
    }

    ...

    private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
        final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
        boolean foundValues = false;
        final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
        for (ResultMapping propertyMapping : propertyMappings) {
            final String column = propertyMapping.getColumn();
            if (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH))) {
                // 获取值
                final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
                Object value = typeHandler.getResult(rsw.getResultSet(), column);
                // 设置值
                final String property = propertyMapping.getProperty();
                if (value != NO_VALUE && property != null && value != null) {
                    // 通过反射工具类设置属性值
                    metaObject.setValue(property, value);
                    foundValues = true;
                }
            }
        }
        return foundValues;
    }
}
  • DefaultResultSetHandler#handlerResultSets 方法开始,调用 handlerResultSet 方法,创建结果处理器、封装数据和保存结果。
  • 那么从封装数据阶段,则包括了创建对象和封装对象属性。

在这里插入图片描述

  • DefaultResultSetHandler#getRowValue 方法中,原有的是通过自动映射,把每列的值赋值到对应的字段上。
  • 而现在因为有了属性映射,所以需要新添加 applyPropertyMappings 方法进行处理。
    • applyPropertyMappings 首先获取 mappedColumnNames 映射的字段。
    • 在后续循环处理 List<ResultMapping> 时,进行比对判断是否包含当前字段。
    • 如果包含则获取到对应类型的 TypeHandler 类型处理器,执行 TypeHandler#getResult 获取结果。
    • 并把这个结果通过 MetaObject 反射工具类把结果设置到对象对应的属性中。

四、测试:ResultMap映射参数

4.1 测试环境配置

4.1.1 在mybatis数据库添加activity数据表

CREATE TABLE `activity`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
  `activity_id` bigint(20) NOT NULL COMMENT '活动ID',
  `activity_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '活动名称',
  `activity_desc` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '活动描述',
  `create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `unique_activity_id`(`activity_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '活动配置' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of activity
-- ----------------------------
INSERT INTO `activity` VALUES (1, 100001, '活动名', '测试活动', '2021-08-08 20:14:50', '2021-08-08 20:14:50');
INSERT INTO `activity` VALUES (3, 100002, '活动名', '测试活动', '2021-10-05 15:49:21', '2021-10-05 15:49:21');

4.1.2 提供DAO接口和实体类

Activity.java

package com.lino.mybatis.test.po;

import java.util.Date;

/**
 * @description: 活动类
 */
public class Activity {

    /**
     * 主键ID
     */
    private Long id;
    /**
     * 活动ID
     */
    private Long activityId;
    /**
     * 活动名称
     */
    private String activityName;
    /**
     * 活动描述
     */
    private String activityDesc;
    /**
     * 创建人
     */
    private String creator;
    /**
     * 创建时间
     */
    private Date createTime;
    /**
     * 更新时间
     */
    private Date updateTime;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getActivityId() {
        return activityId;
    }

    public void setActivityId(Long activityId) {
        this.activityId = activityId;
    }

    public String getActivityName() {
        return activityName;
    }

    public void setActivityName(String activityName) {
        this.activityName = activityName;
    }

    public String getActivityDesc() {
        return activityDesc;
    }

    public void setActivityDesc(String activityDesc) {
        this.activityDesc = activityDesc;
    }

    public String getCreator() {
        return creator;
    }

    public void setCreator(String creator) {
        this.creator = creator;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public Date getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }
}

IActivityDao.java

package com.lino.mybatis.test.dao;

import com.lino.mybatis.test.po.Activity;

/**
 * @description: 活动持久层
 */
public interface IActivityDao {
    /**
     * 根据活动ID查询活动
     *
     * @param activityId 活动ID
     * @return 活动对象
     */
    Activity queryActivityById(Long activityId);
}

4.1.3 配置数据源和配置Mapper

mybatis-config-datasource.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url"
                          value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--XML配置-->
        <mapper resource="mapper/Activity_Mapper.xml"/>
    </mappers>
</configuration>

4.1.4 活动接口配置文件

Activity_Mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lino.mybatis.test.dao.IActivityDao">
    <resultMap id="activityMap" type="com.lino.mybatis.test.po.Activity">
        <id column="id" property="id"/>
        <result column="activity_id" property="activityId"/>
        <result column="activity_name" property="activityName"/>
        <result column="activity_desc" property="activityDesc"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
    </resultMap>

    <select id="queryActivityById" parameterType="java.lang.Long" resultMap="activityMap">
        SELECT activity_id, activity_name, activity_desc, create_time, update_time
        FROM activity
        WHERE activity_id = #{activityId}
    </select>
</mapper>

4.2 单元测试

ApiTest.java

package com.lino.mybatis.test;

import com.alibaba.fastjson.JSON;
import com.lino.mybatis.io.Resources;
import com.lino.mybatis.session.SqlSession;
import com.lino.mybatis.session.SqlSessionFactory;
import com.lino.mybatis.session.SqlSessionFactoryBuilder;
import com.lino.mybatis.test.dao.IActivityDao;
import com.lino.mybatis.test.po.Activity;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;

/**
 * @description: 单元测试
 */
public class ApiTest {

    private Logger logger = LoggerFactory.getLogger(ApiTest.class);

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

    @Test
    public void test_queryActivityById() {
        // 1.获取映射器对象
        IActivityDao dao = sqlSession.getMapper(IActivityDao.class);
        // 2.测试验证
        Activity result = dao.queryActivityById(100001L);
        logger.info("测试结果:{}", JSON.toJSONString(result));
    }
}

测试结果

16:37:14.750 [main] INFO  c.l.mybatis.builder.SqlSourceBuilder - 构建参数映射 property:activityId propertyType:class java.lang.Long
16:37:14.794 [main] INFO  c.l.m.s.defaults.DefaultSqlSession - 执行查询 statement:com.lino.mybatis.test.dao.IActivityDao.queryActivityById parameter:100001
16:37:15.518 [main] INFO  c.l.m.d.pooled.PooledDataSource - Created connention 1516500233.
16:37:15.526 [main] INFO  c.l.m.s.d.DefaultParameterHandler - 根据每个ParameterMapping中的TypeHandler设置对应的参数信息 value:100001
16:37:15.553 [main] INFO  com.lino.mybatis.test.ApiTest - 测试结果:{"activityDesc":"测试活动","activityId":100001,"activityName":"活动名","createTime":1628424890000,"updateTime":1628424890000}

在这里插入图片描述

  • Debug 调试截图中的字段映射匹配和值的填充,测试结果看是通过的。

五、总结:ResultMap映射参数

  • 本章结合整个框架和 ResultMap 提前预留出来的参数解析框架,添加映射类参数的处理操作。
  • 在整个解析的过程中,一个 ResultMap 对应多个 ResultMapping 的关系,把每一条映射都处理成 ResultMapping 信息,都存放到配置项中。
    • 前面提到过 Configuration 伴随着整个 session 生命周期。
  • 所有的解析操作完成以后就是到了接触处理封装中。
    • 思考:怎么把 SQL 执行的结果和对象封装到一起,普通的对象默认按照对象字段即可封装,而带有下划线的属性字段,则需要根据映射的2个字段,下划线对应非下划线的方式,进行匹配处理,最终返回统一的封装对象结果。

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

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

相关文章

数据结构笔记:PR四叉树

1 基本介绍 在PR四叉树中&#xff0c;每个节点代表一个矩形区域&#xff0c;并且每个节点要么没有子节点&#xff0c;要么有四个子节点&#xff0c;分别代表该矩形区域的四个象限 2 数据结构 PR四叉树的每个节点通常包含以下几个元素&#xff1a; 区域&#xff08;矩形&…

go语言基本操作--四

面向对象编程 对于面向对象编程的支持go语言设计得非常简洁而优雅。因为&#xff0c;Go语言并没有沿袭面向对象编程中诸多概念&#xff0c;比如继承(不支持继承&#xff0c;尽管匿名字段的内存布局和行为类似继承&#xff0c;但它并不是继承)、虚函数、构造函数和析构函数、隐…

16字节协议的串口通信

1.协议要求 协议为帧传输&#xff0c;一共16字节。主要是2字节的固定帧头 EB 90&#xff0c;2字节的帧计数(用来计数发出的帧),10字节的数据和2字节的校验位 帧头&#xff1a;2字节&#xff0c;固定值 8’HEB、8’H90 帧计数&#xff1a;2字节&#xff0c;用来说明发出去帧是…

esp32s3实现openmv

演示参考下方视频 源码链接在视频末尾获取点击查看视频 摄像头引脚配置 烧录配置

【Linux】进程基础概念【下篇】

目录 1. 基本概念 2. 常见环境变量 常见环境变量指令 &#xff08;1. PATH &#xff08;2. HOME &#xff08;3. SHELL 3.环境变量的组织形式 &#xff08;1&#xff09;通过代码如何获取环境变量 &#xff08;2&#xff09;普通变量与环境变量的区别 &#xff08;3&…

Vue+NodeJS实现邮件发送

一.邮箱配置 这里以QQ邮箱为例,网易邮箱类似. 设置->账号 二.后端服务搭建 index.js const express require(express) const router require(./router); const app express()// 使用路由文件 app.use(/,router);app.listen(3000, () > {console.log(server…

大学大创项目:手机室内AR导航APP项目思路

文章目录 一、最初的项目思路二、建图和定位分离的项目思路1、建图2、定位 个人见解&#xff0c;如有错误&#xff0c;请多包涵 一、最初的项目思路 在大创项目的开始&#xff0c;将手机确定为应用设备&#xff0c;传感器确定为相机。 由于知识储备的原因&#xff0c;在头一次…

Jenkins实现基础CI操作

操作截图 代码push进gitlab Jenkins拉取gitlab代码 在容器内Jenkins拉取的代码

app备案ios的公钥和md5的获取方法

最近app需要备案才能上架了 但是app备案的时候&#xff0c;特别是ios备案的时候需要提供app的公钥和md5比较头大&#xff0c;无论是android系统还是ios系统&#xff0c;都需要提供证书的公钥和md5。 获取这个公钥和md5真的好麻烦&#xff0c;好像各种工具都没有提供获取这些信…

java数据结构1------深入学习ArrayList

目录 一、概念 二、源码分析 1、属性 2、构造器 ①空构造 ②指定初始容量&#xff08;initialCapacity&#xff09;构造器 ②参数为Collection的构造器 3、常用方法 ①public boolean add(E e) ②public void add(int index, E element) ③其他方法 三、总结 一、概念…

【ARM AMBA5 CHI 入门 12 -- CHI 总线学习 】

文章目录 介绍CHI 特点Layers of the CHI architectureTopology Node TypeTransaction 分类Transaction 路由SAM 介绍Node ID 节点间数据怎么传输的呢&#xff1f; 介绍 CHI 的全称是 Coherent Hub Interface。所以从名字就能看出&#xff0c;CHI要解决什么问题了。按照惯例&a…

思维导图怎么变成ppt?4个思维导图一键生成ppt的方法

做好的思维导图如何变成一份ppt&#xff1f;本文罗列了4个可行方法&#xff0c;一起来看看吧。 一 直接复制粘贴 这是最简单的方法&#xff0c;虽然这样可能会花费一些时间&#xff0c;但可以确保内容排版和布局与你想要的一致。当然&#xff0c;我们大可使用更高效的方法。…

NLP(2)--Transformer

目录 一、Transformer概述 二、输入和输出 三、Encoder 四、Decoder 五、正则化处理 六、对于结构的改进&#xff1f; 七、AT vs NAT 八、Cross-attention 一、Transformer概述 Transformer模型发表于2017年Google团队的Attention is All you need这篇论文&#xff0c;…

七、SSM 框架整合

目前已经学习了 MyBatis 框架&#xff0c;Spring 框架&#xff0c;以及Spring MVC 框架。现阶段学习将这三个框架整合到一起&#xff0c;实现简单的前后端交互的曾删改差功能页面。 Mybatis 框架主要负责数据库的操作问题&#xff0c;以及数据回显。该框架将 SQL 与 Jav…

Browserslist 信息和配置使用整理

我们可以在各种前端工程看到 Browserslist 的配置身影&#xff0c;看似简单但实际上可能会有暗坑导致线上兼容问题&#xff0c;借此文来整理下 Browserslist 的信息。 Browserslist 是由 Autoprefixer 团队维护的一个开源项目&#xff0c;用于自动处理 CSS 和 JavaScript 文件…

opencv识别一张图片的多个红框,并截取红框的内容

需求 需要获取图片的红框的内容&#xff0c;实体的图片我就不放了 获取红框 先截取获得图片的多个轮廓 import cv2 import numpy as np # 加载图像并转换为灰度图像 image cv2.imread(image6.jpg) gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 应用高斯模糊以减…

基本的 Linux 命令以及 Linux 目录结构

目录 什么是Linux&#xff1f; ls - 列出文件和目录 pwd - 显示当前工作目录 cd - 切换目录 mkdir - 创建目录 touch - 创建空文件 rm - 删除文件和目录 cp - 复制文件和目录 mv - 移动和重命名文件和目录 文件系统基础 Linux 操作系统是开源且强大的操作系统&…

流媒体之推流和拉流

推流&#xff1a;将直播内容推送至服务器的过程 拉流&#xff1a;为服务器已有直播内容&#xff0c;用指定地址进行拉取的过程 什么是推流&#xff1f; 推流&#xff0c;指的是把采集阶段封包好的内容传输到服务器的过程。其实就是将现场的视频信号传到网络的过程。“推流”…

D. Sorting By Multiplication(贪心)

Problem - D - Codeforces 给定一个长度为n的数组a&#xff0c;由正整数组成。 您可以对该数组执行以下操作任意次数&#xff08;可能为零&#xff09;&#xff1a; 选择三个整数l、r和x&#xff0c;使得1≤l≤r≤n&#xff0c;并将满足l≤i≤r的每个ai乘以x。 请注意&#…

操作系统内存(32位为例)

0、OS能使用最大的虚拟内存和物理内存 最大的虚拟内存与寻址总线有关。一般是40根&#xff0c;对应256T 最大的物理内存与PTE的位数有关。 10-10-12分页模式下是32位&#xff0c;所以最大寻址空间就4G 1、CPU分页模式 分类 还有5-level&#xff0c;一般适用于大型服务器。…