mybatis核心数据结构详解——ResultMap和ResultMapping

先来思考这样一个问题:

我们使用SQL语句查询出来的表永远是二维的,水平是一个个字段,垂直是一条条记录对应字段的值。(如图)mybatis核心数据结构详解——ResultMap和ResultMapping_第1张图片

但是把结果集映射到对象的过程中,我们会发现——类是存在嵌套结构的。(如图)mybatis核心数据结构详解——ResultMap和ResultMapping_第2张图片

mybatis是如何很好的把二维表中的数据映射到带有嵌套结构的对象中的呢?

由此引出了ResultMap(org.apache.ibatis.mapping.ResultMap)类——结果映射。

它是结果集中的字段与类中属性的映射关系的集合,也就是结果集与类的映射关系。

下面举例说明,如何配置这种映射关系,以及映射关系在内存中的数据结构:

假设有三张表,分为为category分类表,topic话题表以及question问题表。category与topic之间是一对多的关系,topic与question之间是一对多的关系。那么关于topic表到对象之间的映射关系的配置如下:

<resultMap type="com.lixin.domain.Topic" id="topic" autoMapping="true" extends="">
		<id property="id" column="topic_id"/>
		<result property="content" column="topic_content"/>
		<result property="categoryId" column="category_id"/>
		<association property="category" javaType="Category">
			<id property="id" column="category_id"/>
			<result property="content" column="category_content"/>
		</association>
		<collection property="questionList" javaType="list" ofType="Question" >
			<id property="id" column="question_id"/>
			<result property="content" column="question_content"/>
			<result property="topicId" column="topic_id"/>
		</collection>
</resultMap>

resultMap元素的type属性对应要生成的结果对象的类型,id为此ResultMap对象的唯一标识,autoMapping为是否开启自动映射,为结果集中未定义过映射关系的字段进行映射。另外,ResultMap之间可以继承,继承的实际就是父ResultMap中的属性映射集合。resultMap元素的子元素代表指定属性与指定字段之间的映射关系。这种映射关系共分为两种:

  1. 一对一(一个字段值映射到一个属性值中),对应<id><result>。

  2. 一对多(多个字段值映射到同一个属性对象中),对应<association><collection>。这种映射关系,需要额外的ResultMap对象,来描述字段与属性内的各个属性之间的映射关系,也就是nestedResultMap嵌套的结果映射。

ReulstMap只是字段与属性之间的映射关系的集合,那么,真正的字段与属性之间的映射关系,由ResultMapping(org.apache.ibatis.mapping.ResultMapping)属性映射来描述。


ResultMap数据结构如下:

package org.apache.ibatis.mapping;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import org.apache.ibatis.session.Configuration;

/**
 * 结果映射,保存着表与对象之间的映射关系
 * 
 * @author Administrator
 *
 */
public class ResultMap {
        // 对应<resultMap>的id属性
	private String id;
	// 对应<resultMap>的type属性
	private Class<?> type;
	// 对应除<discriminator>元素外的所有属性映射关系
	private List<ResultMapping> resultMappings;
	// 对应所有属性映射中带有ID标志的映射关系,包括<id>元素和<constructor>的<idArg>子元素
	private List<ResultMapping> idResultMappings;
	// 对应所有属性映射中带有Constructor标志的映射关系,包括<constructor>所有子元素
	private List<ResultMapping> constructorResultMappings;
	// 对应所有属性映射中不带有Constructor标志的映射关系
	private List<ResultMapping> propertyResultMappings;
	// 对应所有属性映射中的column属性的集合
	private Set<String> mappedColumns;
	// 鉴别器,对应<discriminator>元素
	private Discriminator discriminator;
	// 是否含有嵌套的结果映射,
	// 如果某个属性映射存在resultMap属性,且不存在resultSet属性,则为true
	private boolean hasNestedResultMaps;
	// 是否含有嵌套查询,
	// 如果某个属性映射存在select属性,则为true
	private boolean hasNestedQueries;
	// 是否开启自动映射
	private Boolean autoMapping;
}

ResultMapping数据结构如下:

package org.apache.ibatis.mapping;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;

/**
 * 字段映射 一个ResultMapping实例对应ResultSet中一个字段到javaBean中一个属性的映射关系
 * 
 * sql元素中,除了<discriminator>子元素以外的其他元素都会生成此类型实例,
 * 其中包括:
 * <idArg><arg><id><result><association><collection>
 * 
 * 内部包含Builder类,负责数据的整合和校验,不负责传入参数的解析
 * @author Administrator
 *
 */
public class ResultMapping {
	// 核心配置对象
	private Configuration configuration;
	// 属性名,对应元素的property属性
	private String property;
	// 字段名,对应元素的column属性
	private String column;
	// 属性的java类型,对应元素的javaType属性
	private Class<?> javaType;
	// 字段的jdbc类型,对应元素的jdbcType属性
	private JdbcType jdbcType;
	// 类型处理器,对应元素的typeHandler属性
	private TypeHandler<?> typeHandler;
	
	// 对应元素的resultMap属性应用名称空间后的结果
	private String nestedResultMapId;
	// 对应元素的select属性应用名称空间后的结果
	private String nestedQueryId;
	// 对应元素的notNullColumn属性拆分后的结果
	private Set<String> notNullColumns;
	// 对应元素的columnPrefix属性
	private String columnPrefix;
	// 处理后的标志,标志共两个:id和constructor
	private List<ResultFlag> flags;
	// 对应元素的column属性拆分后生成的结果,composites.size()>0会时column为null
	private List<ResultMapping> composites;
	// 对应元素的resultSet属性
	private String resultSet;
	// 对应元素的foreignColumn属性
	private String foreignColumn;
	// 是否延迟加载,对应元素的fetchType属性值,lazy则为true否则为false
	// 也直接从配置对象中读取
	private boolean lazy;
}

通用属性配置(每个属性映射元素都包含的属性)如下:

  1. property:结果对象中要映射到的目标属性的属性名称

  2. javaType:结果对象中要映射到的目标属性的类型的Class对象

  3. column:结果集中被映射的字段的名称

  4. jdbcType:结果集中被映射的字段的类型,只是用来与javaType一起确定类型处理器的

  5. typeHandler:类型处理器,负责从结果集中取出某个字段的值,并转为指定的java类型

对象属性值有3种来源:(优先级从高到低)

  1. 来自于一个新的select语句产生的新结果集(通过select属性引用另一个<select>元素的id)

  2. 来自于多余的结果集对象中,这种来源的前提是allowMultiQueries=true允许多sql查询。还需要<select resultSets="rs1,rs2">给每个结果集设置一个名称。在属性映射中配置resultSet属性引用结果集名称(如:<association resultSet="rs2">

  3. 来自于结果集中的若干个字段(根据resultMap映射关系)或某一个(根据column属性值)

当不存在select属性时,映射逻辑为:

    把结果集中column的值映射到结果对象的column。当select属性不存在时,如果也没有显示指定resultMap,则mybatis会根据属性映射元素的所有子元素生成一个内部的ResultMap对象。在applyPropertyMappings应用属性映射时,如果某个属性映射存在resultMap,则column属性会失效。

而当存在select属性时,映射逻辑为:

    把结果集中column的值作为参数对象,传入新的select语句。此时column的值支持两种格式:

  1. 普通格式,即值就对应结果集中的字段名,如:<association column="category_id">。结果集中的字段值作为参数对象(根对象)传入。

  2. 复合格式,property=column,如:<assocication column="categoryId=category_id">。内部会把"{}=,空格"这5个字符作为分隔符对字符串进行拆分,然后按照先属性名后字段名的顺序处理。结果集中的各个字段值作为参数对象指定属性传入(根对象的属性)。

总结一下上一段中所说就是:

        在嵌套属性映射(<association><collection>两种元素)中,column属性默认无效(因为存在resultMap),只有当存在select属性时,column属性才有效。且select属性和resultMap属性不能同时存在。

你可能感兴趣的:(mybatis核心数据结构详解——ResultMap和ResultMapping)