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

沈阳网站建设syfzkj太原百度公司地址

沈阳网站建设syfzkj,太原百度公司地址,技术支持骏域建设网站,网站开发公司按时交付文章目录 一、目标:ResultMap映射参数二、设计:ResultMap映射参数三、实现:ResultMap映射参数3.1 工程结构3.2 ResultMap映射参数类图3.3 添加类型处理器3.3.1 日期类型处理器3.3.2 类型处理器注册机 3.4 存放映射对象3.4.1 结果标志3.4.2 结…

文章目录

  • 一、目标: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个字段,下划线对应非下划线的方式,进行匹配处理,最终返回统一的封装对象结果。
http://www.mmbaike.com/news/33949.html

相关文章:

  • 做网站图片大小电商网站卷烟订货流程
  • 餐饮公司网站建设策划书百度云登陆首页
  • 怎样用ps做网站如何开发自己的小程序
  • 海外百度云网站建设哪家网站推广好
  • wordpress图标不显示重庆seo整站优化外包服务
  • 网站关键词分析工具网站seo诊断工具
  • 企业网站建设哪家便宜广告策划书
  • 网站建设进度上海网上推广
  • 临城企业做网站河北优化seo
  • 做网站公司怎么找客户网站免费搭建平台
  • 从化网站建设公司兰州关键词快速上首页排名
  • 网站制作销售术语怎样建立一个网络销售平台
  • 上海外包软件开发北京seo网络优化招聘网
  • 做钢结构网站有哪些推广网站文案
  • 动态网站建设报告微信营销的方法有哪些
  • 广州今日热点新闻头条资源网站快速优化排名
  • 单位建设网站nba今日最新消息
  • 企业邮箱哪家安全榆林百度seo
  • 海珠网站建设小区推广最有效的方式
  • 怎么做淘宝客的跳转网站搜索引擎营销有哪些
  • 网站虚拟主机公司网站的作用
  • 玩家自助充值网站建设二级域名查询入口
  • 品牌建设实施细则优化关键词的作用
  • 可以做公众号封面图的网站成都网站优化公司
  • 温州网站建设推广专家网站到首页排名
  • 网站建设合同详细小程序开发制作
  • 网站建设课程教学改革网络营销软件大全
  • 成都sem优化seo排名谁教的好
  • 做公司网站哪家好做seo推广公司
  • 做网站找那个公司免费建站平台