Mybatis源码分析(五)--ResultSetHandler分析
学习前的疑问
- Mybatis结果映射有哪几种形式?
- ResultMap的xml所对应的数据结构是什么?
- ResultMap是如何构造的,其中包含继承以及
association
,collection
等标签是如何处理的? - ResultMap在
Executor
执行后是怎么对结果处理的? - 插入写回主键是如何实现的?支持批量吗?
Mybatis结果映射有哪几种形式?
简单来说有两种形式,resultMap
与resultType
.resultType
直接对应一个单一的Java类.resultMap
则对应的是自定义映射规则,并且还可加入association
,collection
等标签完成其他操作.
Very Complex Result Map
1 | <resultMap id="detailedBlogResultMap" type="Blog"> |
ResultMap所对应的数据结构
在org.apache.ibatis.mapping
包下有ResultMap
和ResultMapping
类,按照Mybatis默认的命名规则ResultMap
则是一个xml钟ResultMap的数据结构,而其中的每一个映射配置则是ResultMapping
,如下图.具体的属性也就是xml中所能使用的元素.
结果映射是如何构造的
Mybatis属于交互式框架,也就是说他会在使用前把绝大多数所需要的环境准备好,使用时直接取出来对应的东西.
Mybatis中每一个sql方法最终都会对应一个MappedStatement
对象,该对象的属性就是执行该sql所需要的交互环境.该对象中有`private List
使用resultType配置结果映射
在org.apache.ibatis.builder.MapperBuilderAssistant#getStatementResultMaps
方法中有对resultType的处理.其构造完成后则形成如下图所示的ResultMap对象.
1 | ResultMap inlineResultMap = new ResultMap.Builder( |
使用resultMap配置结果映射
首先resultMap中大概会有如下的元素
1 | constructor - 类在实例化时,用来注入结果到构造方法中 |
同样在org.apache.ibatis.builder.MapperBuilderAssistant#getStatementResultMaps
方法中,有如下这样的逻辑,该操作是去configuration
中获取已经生成好的resultMap,然后直接保存到MappedStatement
对象中.取出来的命名则为statmentId+点+resultMapId
1 | if (resultMap != null) { |
那么configuration
对象中的resultMap只能是解析的时候配置进来的.
在org.apache.ibatis.builder.xml.XMLMapperBuilder#resultMapElement(org.apache.ibatis.parsing.XNode, java.util.List<org.apache.ibatis.mapping.ResultMapping>)
方法中有着解析的逻辑.
代码太长就不贴了,可以自己去看源码.
每一个子节点都是一个ResultMapping
,那么在构造ResultMap
之前,这里会先构造ResultMapping
. 这里的逻辑对constructor
,discriminator
,id
三个字段做了特殊处理.对于其他字段一视同仁.
下面代码根据ResultMap中的节点构造ResultMapping
,那么一视同仁的行为自然在buildResultMappingFromContext()
方法中.
1 | for (XNode resultChild : resultChildren) { |
这里如果想验证你的想法,那么可以注释掉Mybatis对于xml校验的逻辑org.apache.ibatis.parsing.XPathParser#createDocument
中factory.setValidating(validation)
这一行语句,然后你会发现result
,association
,collection
等字段其实都是等价的.互相换也不会影响到结果.具体过程感兴趣可以仔细看代码.ResultMapping
准备完后接下来就是构造ResultMap
,具体逻辑在org.apache.ibatis.builder.ResultMapResolver#resolve
方法中.由于上下文环境已构建好,这里的逻辑只是把一堆ResultMapping
整理,放到对应的属性中.那么ResultMap
构造完毕.
ResultMap在Executor执行后是怎么对结果处理的?
前面的流程把上下文也就是MappedStatement
对象构造好,查询完后由DefaultResultSetHandler
进行取出转换.
在org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleResultSets
方法中有着转换的逻辑.
其中转换主要是三件事
第一找出正确的ResultMap.(org.apache.ibatis.executor.resultset.DefaultResultSetHandler#resolveDiscriminatedResultMap
)
第二对于每一行都使用该ResultMap处理(org.apache.ibatis.executor.resultset.DefaultResultSetHandler#getRowValue
)
第三对于每一列使用ResultMapping处理.(org.apache.ibatis.executor.resultset.DefaultResultSetHandler#applyPropertyMappings
)
最终形成结果.
插入写回主键是如何实现的?支持批量吗?
写回主键主要是在插入时执行KeyGenerator
的processAfter()
方法,从Statement
中拿回主键.
拿回主键写入到输入参数中,那么这里自然也需要拿到输入参数
Mybatis写回主键拿到输入参数
从代码中可以看出Mybatis支持Object,collection,list,array形式的输入参数,另外就是要求构建参数的Map中存在对应的keycollection,list,array
,关于这一点可以看我之前关于构建输入参数的文章分析.
也就是Mybatis支持批量插入返回主键.
1 | private Collection<Object> getParameters(Object parameter) { |
另外一点这里是依赖JDBC驱动的,使用前要先确定你的数据库是否支持写回多个主键.
1 | for (Object parameter : parameters) { |
总结
有点记流水账的感觉,源码分析文章只能起到指引到作用,具体还是自己查看源代码写Test Case,然后一路Debug才能理解深刻.
最后分享下个人的Mybatis阅读仓库项目,使用的是Mybatis的3.4.6-SNAPSHOT版本,数据库则是H2,使用者直接在mybatis-demo
中写测试用例即可.
github: https://github.com/mrdear/Study-Mybatis
- 版权声明: 感谢您的阅读,本文由屈定's Blog版权所有。如若转载,请注明出处。
- 文章标题: Mybatis源码分析(五)--ResultSetHandler分析
- 文章链接: https://mrdear.cn/posts/framework-mybatis-result-set-handler.html