目录
2.1 handleRowValuesForSimpleResultMap 方法对行进行映射处理
2.2 getRowValue方法针对读取ResultSet对象进行映射
1. 简介
本篇是对上一篇的补充,建议先看上一篇了解整个映射流程,然后再看本篇了解详细的映射规则。
在上一篇MyBatis源码分析_ResultSetHandler(7)_chen_yao_kerr的博客-CSDN博客的第4段落,我们已经介绍了无嵌套结果集的映射规则。其实,映射分为2种,有嵌套结果集的和无嵌套结果集的。
2. 无嵌套映射关系
什么叫无嵌套映射关系呢?简单点说就是单表查询的结果,不涉及内、外连接。比如:
我们以TUser表单表查询为例,这就属于无嵌套的查询。而mybatis针对这些无嵌套映射的结果查询,就是用简单的映射规则,如下:
而它走的就是无嵌套的特有方法逻辑,如下:
下面的逻辑,则和上一篇MyBatis源码分析_ResultSetHandler(7)_chen_yao_kerr的博客-CSDN博客的第四段落相同,都是关于无嵌套映射的处理逻辑。
2.1 handleRowValuesForSimpleResultMap 方法对行进行映射处理
而这个方法的核心逻辑就是getRowValue方法,它是获取行数据的。
2.2 getRowValue方法针对读取ResultSet对象进行映射
其实,它就干了以上的4个核心步骤的活,就完成了一个最简单的Pojo对象的映射工作。下面针对3和4两个步骤,详细的看一下没有配置ResultMap和配置了ResultMap的逻辑有何不同之处。
2.2.1 没有配置ResultMap的进行自动映射
所谓的没有配置ResultMap的SQL,mybatis会进行自动填充,填充的column和property值是一样的。这就是我们所谓的自动映射 (字段名 和 Pojo的属性名 必须一致)
自动填充完映射规则以后,我们需要根据自行填充的映射规则进行映射。其实,逻辑就是根据column去ResultSet中获取值,然后把值按照property属性设置到POJO中。而POJO则是封装在MetaObject对象中
2.2.2 配置ResultMap的映射
如果已经有了ResultMap的映射规则,那么我们是不会进行自动填充映射规则的。也就是说上面的自动映射逻辑不会走的。
我们会根据在xml中配置的ResultMap信息,直接获取到xml配置好的映射规则。具体逻辑就是先获取到column,然后根据column获取到封装 映射规则的 ResultMapping 对象。
而正确的设置值的地方在下面的 applyPropertyMappings 方法中
这边稍微提一下TypeHandler对象,它就是根据不同类型,用不同的方式从ResultSet中获取值的。而TypeHandler的类型有很多,比如IntergerTypeHandler:
按照模板设计原则,我们只需要调用父类的公用方法,然后就会调到具体的子类的实现方法中去,如下:
由于ID是Integer类型的,因此它实际调用的是IntegerTypeHandler的getResult方法。而最终,必然调用resultSet的getInt方法
而如果是一个String类型的字段,比如userName。它则会调用到StringTypeHandler类中的此方法。 因为userName是字符串类型,它必然调用resultSet的getString方法。
这就是TypeHandler的妙用。
自此,无嵌套的结果集映射就全部讲完了。
3. 有嵌套的映射关系
嵌套关系,我们日常开发经常遇到。而mybatis处理嵌套映射,则是映射关系中比较核心的逻辑了。而在mybatis中,针对 一对一的嵌套,和 一对多的嵌套,使用的是不同的标签。association是一对一的嵌套,而 collection 是一对多的嵌套。
针对有嵌套的逻辑,我们走的逻辑肯定也是不一样的,下面我们以 一对多 的嵌套关系为例进行说明,如下:
代码部分:
3.1 嵌套的行数据 getRowValue方法
开始之前,我先介绍2个缓存,因为我觉得这2个缓存很重要 :
第一个缓存 nestedResultObjects,它是负责缓存行数据的。只要是处理过的行,都会被缓存
第二个是缓存所有的结果集数据的,简单的就是它负责存储从数据库中查到的数据。我们最终返回的就是这个list,上层方法拿到这个list处理并且最终返回即可。
ok, 言归正传,下面开始我们的嵌套映射源码分析阶段
1. 至于生成MetaObject的逻辑,它是和无嵌套逻辑一样的,没什么好说的。
2. 对于TUser表的映射,还是走applyPropertyMappings方法,和上面的一样,没什么好说的
3. 嵌套映射,核心在嵌套关系上。
比如TUser表下嵌套Job表,那么TUser表还是原有的逻辑,而Job表会有特有的逻辑来处理,也就是一对一的关系,然后把TUser表放入缓存中,注意此时TUser表已经嵌套了Job表了。
当第二次再来处理的时候,我们会从缓存中拿到之前的TUser表信息,所有就不会再次生成TUser表了,但是Job表示新的,所有我们会按照映射规则再次生成Job对象,放入TUser表的List中。此时,一条TUser表嵌套了多条Job表的数据,这就是一对多的关系
3.1.1 针对TUser表的映射
我们以id为2的数据为例进行分析:
1. 首先,我们进入getRowValue方法内部会映射TUser表,也就是把id为2的数据进行映射。但是,此时不会去映射 Job表的信息
2. 进入处理嵌套关系的映射方法 applyNestedResultMappings 以后,其实就是获取到单独的Job表对象,然后按照之前的逻辑再给映射一遍数据而已。
待我们的Job表初始化完成以后,我们会再次判断这个Job表有没有嵌套关系,如果有嵌套的话,再次进入applyNestedResultMappings方法,进行嵌套类的映射,直到没有映射关系为止。
3. applyNestedResultMappings方法处理完嵌套类的映射以后,我们会进入 linkObjects方法。
4. instantiateCollectionPropertyIfAppropriate 这个方法比较有意思,它是根据映射规则,把TUser表中的字段 jobs 给实例化。而我们TUser表中的 jobs 是一个ArrayList类型的。因此,它也会生成一个ArrayList对象,存储 Job 数据
这个地方,我又要补一下前面的知识了。
Configuration对象负责生成MetaObject,而MetaObject包装的具体是哪一种类型的Bean呢? 这需要根据传入的实例来确定的。此处传入的是是ArrayList,因此会生成一个集合类型的
既然是集合类型的MetaObject,那么把Job放入MetaObject必然也是ArrayList中的,来看看具体是怎么做到的:
其实,就是简单的把嵌套类 Job放入ArrayList中而已
5. 最后,还是返回到 getRowValue方法中,把TUser给缓存起来。此时的TUser已经含有1条Job的嵌套数据了。
3.1.2 再次处理相同的TUser
1. 此时,我们会直接从缓存中获取到TUser信息,因此不会再次生成TUser对象了。但是,TUser嵌套的Job信息则是从数据库刚查出来的,而且是一个嵌套类,因此嵌套处理还时要继续的
来看看第二次进来和第一次进入有何区别:
2. 其实,就是根据当前包装TUser的MetaObject对象,去实例化Jobs这个变量。此时,我们发现TUser对象的 jobs 变量已经被实例化了。就直接拿到这个 jobs变量而已,此处是ArrayList。我们还发现,这个ArrayList里面已经存储了一条Job数据,直接使用这个ArrayList并且把刚查到的另一条关于 Job的数据放入ArrayList中,完成一对多的操作