当前位置: 首页 > news >正文

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

文章目录

  • 一、目标: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> {@Overrideprotected void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {ps.setTimestamp(i, new Timestamp((parameter).getTime()));}@Overrideprotected 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 herereturn (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.queryUserInfoByIdid = applyCurrentNamespace(id, false);MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlCommandType, sqlSource, resultType);// 结果映射, 给 MappedStatement#resultMapssetStatementResultMap(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 返回结果,所以这里返回的是 nullresultMap = 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 + activityMapid = 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);// 绑定映射器到namespaceconfiguration.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.配置namespaceString namespace = element.attributeValue("namespace");if ("".equals(namespace)) {throw new RuntimeException("Mapper's namespace cannot be empty");}builderAssistant.setCurrentNamespace(namespace);// 2.解析resultMapresultMapElement(element.elements("resultMap"));// 3.配置select|insert|update|deletebuildStatementFromContext(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);}// 构建 ResultMappingresultMappings.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 元素列表*/@SafeVarargsprivate 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_timeFROM activityWHERE 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;@Beforepublic void init() throws IOException {// 1.从SqlSessionFactory中获取SqlSessionSqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config-datasource.xml"));sqlSession = sqlSessionFactory.openSession();}@Testpublic 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个字段,下划线对应非下划线的方式,进行匹配处理,最终返回统一的封装对象结果。

相关文章:

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

文章目录 一、目标&#xff1a;ResultMap映射参数二、设计&#xff1a;ResultMap映射参数三、实现&#xff1a;ResultMap映射参数3.1 工程结构3.2 ResultMap映射参数类图3.3 添加类型处理器3.3.1 日期类型处理器3.3.2 类型处理器注册机 3.4 存放映射对象3.4.1 结果标志3.4.2 结…...

GE VME-7807RC-410001350-93007807-410001 K数字输入模块

通道数目&#xff1a; VME-7807RC-410001350-93007807-410001K 数字输入模块通常具有多个数字输入通道&#xff0c;可以同时监测多个数字信号。 输入类型&#xff1a; 这种模块通常用于监测数字信号&#xff0c;例如开关状态&#xff08;ON/OFF&#xff09;或计数器脉冲。 采…...

C++插入加密,替代加密

void 插入加密() {//缘由https://bbs.csdn.net/topics/396047473int n 1, j 0;char aa[60]{}, aaa[] "abcde";cin >> aa;while (j < 60 && (aa[j] - \0))cout << aa[j] << aaa[j % 5]; } void 插入加密() {//缘由https://bbs.csdn.n…...

Web前端开发概述

Web&#xff08;World Wide Web&#xff0c;全球广域网&#xff09;是指一种基于互联网的信息系统&#xff0c;通过超文本链接将全球各地的文档、图像、视频等资源相互关联起来&#xff0c;并通过Web浏览器进行交互浏览和访问。Web的发展使得人们可以方便地获取和共享各种类型的…...

Web自动化 —— Selenium元素定位与防踩坑

1. 基本元素定位一 from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By # selenium Service("../../chromedriver.exe") # driver webdriver.Chrome(serviceService) # driver.…...

【数据结构】树和二叉树的概念及结构(一)

目录 一&#xff0c;树的概念及结构 1&#xff0c;树的定义 2&#xff0c;树结点的分类及关系 3&#xff0c;树的表示 二&#xff0c;二叉树的概念及结构 1&#xff0c;二叉树的定义 2&#xff0c;特殊的二叉树 3&#xff0c;二叉树的性质 4&#xff0c;二叉树的存储结构 1&…...

第三章 USB应用笔记之USB鼠标(以STM32 hal库为例)

第三章 USB应用笔记之USB鼠标&#xff08;以STM32 hal库为例&#xff09; 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 第三章 USB应用笔记之USB鼠标&#xff08;以STM32 hal库为例&#xff09;前言一、STM32 U…...

微服务01-基本介绍+注册中心EureKa

基本介绍 服务集群&#xff1a;一个请求由多个服务完成&#xff0c;服务接口暴露&#xff0c;以便于相互调用&#xff1b; 注册中心&#xff1a;每个服务的状态&#xff0c;需要进行维护&#xff0c;我们可以在注册中心进行监控维护服务&#xff1b; 配置中心&#xff1a;这些…...

【ES6】JavaScript中的异步编程:async和await

在JavaScript中&#xff0c;异步编程是一种处理长时间运行的操作的方法&#xff0c;这些操作包括读取文件、网络请求或处理大数据等。在传统的回调函数中&#xff0c;代码按照顺序执行&#xff0c;一旦遇到长时间运行的操作&#xff0c;就需要回调函数来处理结果。这使得代码变…...

51单片机热水器温度控制系统仿真设计( proteus仿真+程序+原理图+报告+讲解视频)

51单片机热水器温度控制系统仿真设计 1.主要功能&#xff1a;2.仿真3. 程序代码4. 原理图5. 设计报告6. 设计资料内容清单 &&下载链接 51单片机热水器温度控制系统仿真设计( proteus仿真程序原理图报告讲解视频&#xff09; 仿真图proteus7.8及以上 程序编译器&#x…...

Spring Boot 配置文件加密

方式一&#xff1a;Spring Cloud Config 一、建立config server 1. build.gradle 文件中添加&#xff1a; plugins {id javaid org.springframework.boot version 2.7.0id io.spring.dependency-management version 1.0.11.RELEASE }ext {set(springCloudVersion, "202…...

【树形权限】树形列表权限互斥选择、el-tree设置禁用等等

需求&#xff1a;按照权限管理配置的数据权限树展开&#xff1b;点击查看按钮后进入其他指定机构选择弹窗为一树形结构 本文章对项目中出现得关键点进行总结。 一、实现如上树形列表 在 element 官方表格示例中&#xff0c;实现树形表格列表数据渲染&#xff0c;非常简单。只…...

ubuntu 22.04安装cuda、cudnn、conda、pytorch

1、cuda 视频连接 https://www.bilibili.com/video/BV1bW4y197Mo/?spm_id_from333.999.0.0&vd_source3b42b36e44d271f58e90f86679d77db7cuda 11.8 https://developer.nvidia.com/cuda-toolkit-archive点击进入 https://developer.nvidia.com/cuda-11-8-0-download-arc…...

2023 最新前端面试题 (HTML 篇)

1. src 和 href 的区别 src 用于替换当前元素&#xff08;引入&#xff09;&#xff0c;href 用于在当前文档和引用资源之间确立联系&#xff08;引用&#xff09; &#xff08;1&#xff09;src&#xff08;source&#xff09; 指向外部资源的位置&#xff0c;指向的内容将会嵌…...

华为云银河麒麟V10安装libmcrypt

本次安装是在华为云上执行。cpu是鲲鹏&#xff0c;操作系统是银河麒麟V10. 先下载安装包&#xff1a; wget http://downloads.sourceforge.net/mcrypt/libmcrypt-2.5.8.tar.gz 解包&#xff0c;进入目录中。 执行如下命令&#xff1a; ./configure make make install 执…...

智慧导览|智能导游系统|AR景区导览系统|景区电子导览

随着文旅市场的加快复苏&#xff0c;以及元宇宙、VR、AR、虚拟数字人等新兴技术的快速发展&#xff0c;文旅行业也正在加快数字化转型的步伐&#xff0c;向智慧景区建设迈进。为满足不同年龄段游客的游览需要&#xff0c;提升旅游服务体验&#xff0c;越来越多的旅游景区、博物…...

【Docker】Docker基本使用介绍

Docker是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中&#xff0c;然后发布到任何流行的Linux机器上&#xff0c;也可以实现虚拟化。 一、安装Docker 首先&#xff0c;你需要从官方网站上下载Docker的安装包&#xff0c;并按…...

Linux命令200例:man用于显示和阅读关于Linux内置命令的使用说明

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌。CSDN专家博主&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0…...

idea 无法识别maven的解决

问题描述 从git拉取代码或者修改文件夹以后&#xff0c;整个项目所有依赖爆红无法通过修改或者重新加载maven解决版本为idea 2021 问题定位 maven的版本太高&#xff0c;而idea的般本太低&#xff0c;导致识别的时候稳定性差 解决 使用idea原生的maven版本 选择已捆绑的m…...

String底层函数的实现方式

一、常见的String封装函数 1. strcpy函数的实现 char *strcpy(char *dest, const char *src) {char *tmp dest;while ((*dest *src) ! \0)/* nothing */;return tmp; } 2. strncpy函数的实现 char *strncpy(char *dest, const char *src, size_t count) {char *tmp dest…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad&#xff08;Adaptive Gradient Algorithm&#xff09;是一种自适应学习率的优化算法&#xff0c;由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率&#xff0c;适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI

前一阵子在百度 AI 开发者大会上&#xff0c;看到基于小智 AI DIY 玩具的演示&#xff0c;感觉有点意思&#xff0c;想着自己也来试试。 如果只是想烧录现成的固件&#xff0c;乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外&#xff0c;还提供了基于网页版的 ESP LA…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

Selenium常用函数介绍

目录 一&#xff0c;元素定位 1.1 cssSeector 1.2 xpath 二&#xff0c;操作测试对象 三&#xff0c;窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四&#xff0c;弹窗 五&#xff0c;等待 六&#xff0c;导航 七&#xff0c;文件上传 …...

论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing

Muffin 论文 现有方法 CRADLE 和 LEMON&#xff0c;依赖模型推理阶段输出进行差分测试&#xff0c;但在训练阶段是不可行的&#xff0c;因为训练阶段直到最后才有固定输出&#xff0c;中间过程是不断变化的。API 库覆盖低&#xff0c;因为各个 API 都是在各种具体场景下使用。…...

前端中slice和splic的区别

1. slice slice 用于从数组中提取一部分元素&#xff0c;返回一个新的数组。 特点&#xff1a; 不修改原数组&#xff1a;slice 不会改变原数组&#xff0c;而是返回一个新的数组。提取数组的部分&#xff1a;slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...

Modbus RTU与Modbus TCP详解指南

目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...