【MyBatis源码分析】select源码分析及小结
- 作者: 五速梦信息网
- 时间: 2026年04月04日 13:56
示例代码
之前的文章说过,对于MyBatis来说insert、update、delete是一组的,因为对于MyBatis来说它们都是update;select是一组的,因为对于MyBatis来说它就是select。
本文研究一下select的实现流程,示例代码为:
selectMailById方法的实现为:
我们知道MyBatis提供的select有selectList和selectOne两个方法,但是本文只分析且只需要分析selectOne方法,原因后面说。
selectOne方法流程
先看一下SqlSession的selectOne方法流程,方法位于DefaultSqlSession中:
这里就是为什么我说selectOne与selectList两个方法只需要分析selectList方法就可以了的原因,因为在MyBatis中所有selectOne操作最后都会转换为selectList操作,无非就是操作完毕之后判断一下结果集的个数,如果结果集个数超过一个就报错。
接着看下第3行的selectList的代码实现,方法同样位于DefaultSqlSession中:
第3行获取MappedStatement就不说了,跟一下第4行Executor的query方法实现,这里使用了一个装饰器模式,给SimpleExecutor加上了缓存功能,代码位于CachingExecutor中:
第2行的代码获取BoundSql,BoundSql中的内容上文已经说过了,最后也会有总结。
第3行的代码根据输入参数构建缓存Key。
第4行的代码执行查询操作,看下代码实现,代码同样位于CachingExecutor中:
这里并没有配置且引用Cache,因此不执行第4行的判断,执行第17行的代码,代码位于SimpleExecutor的父类BaseExecutor中,源码实现为:
这里执行第16行的代码,queryFromDatabase方法实现为:
代码走到第5行,最终执行duQuery方法,方法的实现为:
看到第4行~第6行的代码都和前文update是一样的,就不说了,handler有印象的朋友应该记得是PreparedStatementHandler,下一部分就分析一下和update的区别,PreparedStatementHandler的query方法是如何实现的。
PreparedStatementHandler的query方法实现
跟一下PreparedStatementHandler的query方法跟到底,其最终实现为:
看到第3行执行查询操作,第4行的代码处理结果集,将结果集转换为List,handleResultSets方法实现为:
总结一下这个方法。
第7行代码,通过PreparedStatement的getResultSet方法获取ResultSet,并将ResultSet包装为ResultSetWrapper,ResultSetWrapper除了包含了ResultSet之外,还依次定义了数据库返回的每条数据的每行列名、列对应的JDBC类型、列对应的Java Class的类型,除此之外最主要的是还包含了TypeHandlerRegister(类型处理器,所有的参数都是通过TypeHandler进行设置的)。
第9行代码,获取该<select>标签中定义的ResultMap,不过这里我有点没弄明白,一个<select>标签按道理应该只能定义一个resultMap属性,但是这里却获取的是一个List<ResultMap>,不是很清楚。
第11行代码,做了一个校验,即如果select出来有结果返回,但是没有ResultMap或者ResultType与之对应的话,抛出异常,道理很简单,没有这2者之一,MyBatis并不知道将返回转成什么样子。
第12行~第18行的代码,将ResultSetWrapper中的值根据ResultMap,转成Java对象,先存储在multipleResults中,这是一个List<Object>。
第20行~第33行的代码,是用于处理<select>中定义的resultSets的,由于这里没有定义,因此跳过。
第35行的代码,将multipleResults,根据其size大小,如果size=1,获取0号元素,强转为List<Object>;如果size!=1,直接返回multipleResults。
总得来说这个方法,根据数据库返回的结果,封装为自定义的ResultMap的流程基本是没问题的,只是这里的一个问题是,为什么要定义一个multipleResults,最后根据multipleResults的size来判断并拆分最终的结果,还没有完全搞懂,这部分还要留待后面的工作中随着MyBatis应用的深入再去学习。
小结
前文已经对MyBatis配置文件加载、CRUD操作都进行了分析,就从我自己的感觉来说,对整个流程基本有数,但是很多地方感觉还是有些印象不深,最主要的就是从什么地方获取什么数据,获取的数据在什么地方使用,因此这里做一个总结加深印象,主要总结的是MyBatis中重点的类中持有哪些内容。
首先是SqlSessionFactory,默认使用的是DefaultSqlSessionFactory,我们使用它来每次打开一个SqlSession,SqlSessionFactory持有:

接着是Configuration,它是所有配置信息最终存储的位置,其中大部分的属性尤其是布尔型值都可以通过<settings>标签进行配置,任何的操作(如打开一个SqlSession、执行增删改查等)都要从Configuration中拿相关信息,Configuration持有的一些重要属性有:

接着是Environment,它存储的是配置的数据库环境信息,可以指定多个,但是最终只能使用一个,Environment持有的一些重要属性有:

接着是MappedStatement,一个MappedStatement对应mapper文件中的一个<insert>、<delete>、<update>、<select>,每次执行MyBatis操作的时候先获取对应的MappedStatement,MappedStatement持有的一些重要属性有:

接着是BoundSql,BoundSql中最重要存储的就是当前要执行的SQL语句,其余还有要设置的参数信息与参数对象,BoundSql持有的属性有:

最后是ParameterMapping,ParameterMapping是待设置的参数映射,存储了待设置的参数的相关信息,ParameterMapping持有的属性有:

MyBatis中使用到的设计模式
下面来总结一下MyBatis中使用到的设计模式,有些设计模式可能在到目前位置的文章中没有体现,但是在之后的【MyBatis源码分析】系列文章中也会体现,这里一并先列举出来:
1、建造者模式
代码示例为SqlSessionFactoryBuilder,代码片段:
重载了大量的build方法,可以根据参数的不同构建出不同的SqlSessionFactory。
2、抽象工厂模式
代码示例为TransactionFactory,代码片段为:
抽象出事物工厂,不同的事物类型实现不同的事物工厂,像这里就是Jdbc事物工厂,通过Jdbc事物工厂去返回事物接口的具体实现。
其它的像DataSourceFactory也是抽象工厂模式的实现。
3、模板模式
代码示例为BaseExecutor,代码片段:
BaseExecutor封装好方法流程,子类例如SimpleExecutor去实现。
4、责任链模式
代码示例为InterceptorChain,代码片段为:
可以根据需要添加自己的Interceptor,最终按照定义的Interceptor的顺序逐一嵌套执行。
5、装饰器模式
代码示例为CachingExecutor,代码片段为:
给Executor添加上了缓存的功能,update与query的时候会根据用户配置先尝试操作缓存。
在MyBatis中还有很多地方使用到了装饰器模式,例如StatementHandler、Cache。
6、代理模式
代码示例为PooledConnection,代码片段为:
这层代理的作用主要是为了让Connection使用完毕之后从栈中弹出来。
MyBatis中的插件也是使用代理模式实现的,这个在后面会说到。
相关文章
-
-
【MySQL】测试MySQL表中安全删除重复数据只保留一条的相关方法
【MySQL】测试MySQL表中安全删除重复数据只保留一条的相关方法
- 互联网
- 2026年04月04日
-
【MySQL】排序原理与案例分析
【MySQL】排序原理与案例分析
- 互联网
- 2026年04月04日
-
【MyBatis】MyBatis分页插件PageHelper的使用
【MyBatis】MyBatis分页插件PageHelper的使用
- 互联网
- 2026年04月04日
-
【MVC】关于Action返回结果类型的事儿(上)
【MVC】关于Action返回结果类型的事儿(上)
- 互联网
- 2026年04月04日
-
【Mock平台】测试开发实战01
【Mock平台】测试开发实战01
- 互联网
- 2026年04月04日






