Mybatis原理解析(四)--查询流程

前三篇Mybatis的原理解析可以点击链接查看:

Mybatis原理解析(一)解析全局xml配置文件生成SqlSessionFactory过程

Mybatis原理解析(二)SqlSession的创建过程

Mybatis原理解析(三)--getMapper动态获取接口实现类

这一篇主要讲的是在前三篇的基础上Mybatis的查询流程(与增删改同理),也是Mybatis中最为核心的知识,话不多说,debug为敬。

我们在第三篇中说了我们得到的Mapper接口对象其实是一个代理实现类,被代理的是MapperProxy对象,而MapperProxy对象并没有实现我们的Mapper接口,所以说当我们调用代理类的接口方法时,invoke方法会给出一个条件判断是否是Object类的方法(如果这里没有这个判断那么程序会报错,详情可以看第三篇的解释),很明显我们调用的是接口的方法,不是Object的方法,所以我们的程序直接来到cachedMapperMethod方法,深入这个方法

 在这里其实就是做了个优化,通过一个map容器去缓存我们的MapperMethed,key为调用的接口方法Method,value为MapperMethod。而这个MapperMethod里面主要有两个属性:

其中一个SqlCommand对象,那么我们来看下这个SqlCommand里面有什么,来到它的构造函数

首先它是MapperMethod的一个内部类,里面有两个属性分别是name,type,那么我这里就直接给大家说了这个两个属性的作用了,name属性根据传过来的interface以及method得到我们调用的方法的全路径名称去Configuration里面去拿到对应的MappedStatement,然后再把得到的MappedStatement对象的id赋值给name,而type同理,也是MappedStatement的sqlCommandType赋值得到的。而另外的一个MethodSignature也是MapperMethod的内部类,作用就是判断调用的接口方法的返回是什么类型,这里就不多讲述了。

上面说的这两个类,其实是在为下面做铺垫,我们点击下一步,来到mapperMethod.execute方法,继续深入。

可以看出这里首先就是通过MapperMethod里面的SqlCommand的type去判断方法类型,由于我们这里是一个查询方法,所以来到了SELECT分支中

在SELECT分支中,又去判断方法的返回值,根据方法的返回值不同就去到不同的if分支,而明显这里能有条件去判断就是我们上面说的MethodSignature的功劳了。接下来来到了sqlSession.selectOne方法,传入了方法的全路径名称以及调用的接口方法参数,继续深入。

 深入selectList方法。

继续深入。

 可以看到这里根据了前面sqlCommond的name值去Configuration对象里面得到对应的MappedStatement对象,然后调用executor.query方法,注意这里有一个wrapCollection方法,从名字可以看出应该是给我们的参数对象进行包装,这里不多讲述。然后继续深入query方法。

通过传进来的MappedStatement的getBoundSql方法可以得到BoundSql对象,该对象里面包含有了我们原始的sql语句。而CacheKey是给我们的二级缓存使用的一个key值,继续深入下面的query方法。

 这里调用到了CachingExecutor的query方法,因为我们这里是默认配置了二级缓存的,所以我们真正执行的Executor会被CachingExcutor包装了,这里我们的第二篇里面有讲到了,不再详细讲述。然后如果我们二级缓存中没有数据的话,就要调用打我们真正的executor了。深入delegate.query方法。

这里和二级缓存类似,是先从本地缓存中取,即一级缓存,所以这里我们可以知道Mybatis里面是数据来源顺序是先二级缓存,再一级缓存,最后数据库中取。我们深入到queryFromDataBase这个方法中。 

深入doQuery方法

 此时来到了SimpleExecutor的doQuery方法,看到先从传入的MappedStatement对象拿到Configuration对象,然后调用了configuration的newStatementHandler方法,该方法返回了一个StatementHandler对象,深入这个方法。

可以看到里面先是通过子类创建了一个RoutingStatemnetHandler对象,这个RoutingStatemnetHandler对象是什么尼?我们进去它的构造方法看看。

在它的属性中,有一个StatementHandler的属性对象,其实这个属性对象才是这个类的方法的真正执行对象,即这个RoutingStatementHandler其实就是做了一层包装而已,而创建这个对象是根据我们在配置文件中设置的statementType来确定的,默认是创建一个预编译的PrepareStatementHandler。

然后看上面红框的这句代码interceptorChain.pluginAll(statementHandler),很熟悉的一句代码,其实在我们初始化Executor的时候也出现过这句代码,在后面的代码我们也会看见这一句代码,这是与Mybatis的插件机制相关的。那么这个StatementHandler对象是什么尼?我们先执行看下面的代码。

执行完这个newStatementHandler方法之后,来到了下面的prepareStatement方法,深入这个方法。

可以看出其实我们的StatementHandler对象就是用来获取Statement对象的,而这个Statement对象就是我们在学习JDBC编程的时候jdk里面的Statement对象,所以Mybatis对数据库的操作其实也是封装了JDBC的。下面我们来看下StatementHandler是怎么获取Statement的,深入handler.prepare

可以看到里面是调用了prepareStatement的prepare方法,深入这个方法。

 可以看到这里显示从boundSql里面拿到sql语句,然后调用connection.prepareStatement方法把sql传入去,这样就得到了Statement对象了。

之后就来到了handler.parameterize(stmt),然后我们深入进去看看。

里面调用了parameterHandler的setParameters方法,而这个parameterHandler对象是什么?我们回到之前初始化PreparedStatementHandler那里。

PreparedStatementHandler里面并没有这个parameterHandler对象,而PreparedStatementHandler是继承于BaseStatementHandler,所以初始化parameterHandler对象应该是在其父类BaseStatementHandler的构造方法中。

来到BaseStatementHandler的构造方法,深入configuration.newParameterHandler方法。

可以看出newParameterHandler和newResultSetHandler都有调用到Mybatis插件机制。而目前我们所知的能被Mybatis插件拦截的有Executor,StatementHandler,ParameterHandler,ResultSetHandler这个四个对象,其实能被Mybatis插件拦截的也就这四大对象了在Mybatis中,我们习惯称为这是Mybatis四大组件。

猜你喜欢

转载自blog.csdn.net/weixin_37689658/article/details/99617925