上期我们讲了如何获取Mapper,我们获取到了Mapper之后,就要执行Mapper里面的方法,这篇我们就来详细讲解方法的执行过程
同样使用我们一直用的测试方法:
@Test
public void testFindAll() throws IOException {
//加载主配置文件,目的是为了构建SqlSessionFactory对象
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//通过SqlSessionFactory工厂对象创建SqlSesssion对象
SqlSession sqlSession = factory.openSession();
//通过Session创建UserDao接口代理对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.findAll();
for (User user : userList) {
System.out.println(user);
}
}
上一篇我们分析到了getMapper()
方法,这一篇我们就来分析执行方法的这句代码:
List<User> userList = mapper.findAll();
其实它就是一个jdk动态代理,返回的是一个代理对象,详情往下看,这个时候我们只能通过调试代码来分析了,我们在这句代码打个断点
从上图我们也可以知道,它返回的是一个代理对象,我们跟进去看,使用快捷键F7
,进入执行的方法
当前我们的类是在MapperProxy
类中,它其实是真正的代理类,当执行方法的时候,它会执行我们重写的invoke()
方法,第一句代码判断的执行的方法是不是Object
里面的euqals()
,hashCode()
等等方法,我们接着调试,按F8
我们直接调试到关键语句,这句代码调用了
mapperMethod.execute()
方法,我们按F7
进入方法里面
这个方法首先判断执行的Sql
类型,是增删改查的哪一种,明显这里我们是SELECT
语句
接下来判断方法的返回值类型,看看是返回的是集合还是单个对象
再次调用executeForMany()
方法,我们接着进去这个方法
这个方法首先会做参数转化,参数转化这块后面我会专门写一篇,这里面还是有点东西的,现在我们只要知道这个方法是把我们的参数做处理了,接着往下
这个if判断的是有没有分页条件,显然这里我们没有分页
这里到了我们真正执行的方法的,它调用的是sqlSession
里面的selectList()
方法,我们F7
直接进去
接着进去
首先它获取我们的mappedStatement
它又调用executor
里面的query
方法,我们进去
它再次调用query()
方法
这个方法的逻辑首先判断缓存中有没有(这里是二级缓存),最后调用的是
delegate.query()
,这里使用了装饰器模式,装饰的是delegate
这个对象,我们接着进去
这里我们进入的是BaseExecutor
,而我们的上一级执行的是CacheExetor
,这里我们需要说明一下,在CacheExetor
判断的是二级缓存,BaseExetor
里面执行的是一级缓存的逻辑
我们来到这句代码,这句代码的意思是缓存里面没有,我们需要从数据库里面查询,我们接着进去
它执行了一个doQuery()
方法,我们再直接进去
我们已经走到了SimpleExecutor
类里面,其实它是BaseExetor
的子类,这个方法主要创建StatementHandler
,构建prepareStatement
等等,我们再进去handle.query()
方法
这里没有什么逻辑,接着跟进去
这里就是我们的终极方法了, 这里我们就到了最底层的JDBC
的操作了,说明我们执行了真正的sql语句,但是它返回的是一个ResultSet
,它又是怎么转换为我们的JavaBean
的呢?这个我们就不必关心了,mybaits有它自己的处理器和工具类去完成这个功能
到此我们就完成了查询功能,我们也拿到了我们想要的结果
小结:其实mybatis源码并没有我们想象的那么复杂,它在一些常用的框架里面算是最简单的了,它代码量小,但是结构复杂,它里面的很多巧妙的设计模式是我们值得花时间去学习的