这次我们来说说Mybatis的源码,这里只说执行的流程,内部细节太多了,这里只能授之以渔了。还是最近的那段代码,我们来回顾一下。
package mybatis; import mybatis.bean.StudentBean; import mybatis.dao.StudentMapper; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; public class Test1 { public SqlSession session; public SqlSessionFactory sqlSessionFactory; @Before public void init() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); session = sqlSessionFactory.openSession(); } @Test public void studentTest() { StudentMapper mapper = session.getMapper(StudentMapper.class); StudentBean result = mapper.selectUser(1); System.out.println(result); session.commit(); } }
就是拿到流文件,也是我们主配置文件,进行流文件解析,传入到build内,构建成一个sqlSessionFactory,再由sqlSessionFactory得到session,拿到mapper,执行sql,完成。
简单的流程应该是这样的,我们来一个稍微专业一点图。
转变为我们的代码大概就是这三步,构建Configuration对象,构建我们的sqlsessionfactory,得到我们的session,得到对应的statement(处理参数和结果)从而得到结果集。
那么我们先从我们一步步来看,先看我们的Configuration对象
Configuration解析:
我们打开Configuration类可以看到,里面很多的属性设置,包括缓存,mapper,插件等等,其实就是把我们的xml标签转化为对象了,这里需要说明一下的是,这个解析过程是把所有的相关的xml都转为Configuration对象了,包括config.xml和mapper.xml。源码太多,我就不粘贴了。我给你看一下我转化完成的。
这里没有什么神秘的,就是一个xml解析的过程,在XMLConfigBuilder类的parse方法,生成了完成的Configuration对象,有兴趣的可以打个断点看一下。
这里要注意的就是里面很多元素是一个对应多个的,很多属性是map和set。顺便收一下里面是由多个构造器来构建的。
MapperAnnotationBuilder为注解方式的构造器,其余都是为Configuration服务的。也就是说在创建Configuration之后很多东西就已经确定了(除了cache)。那么又是如何生成sqlsession的呢。回到我们创建完Configuration对象那句源码上来。
点击build方法进去,我们看到是new DefaultSqlSessionFactory然后把我们的Configuration对象传递进去,也就是说有了Configuration对象也就生成了我们的DefaultSqlSessionFactory对象。DefaultSqlSessionFactory实现了我们的SqlSessionFactory,我们也就得到了SqlSessionFactory。接下来就是我们的sqlsession了
sessionsql解析:
还是老规矩,上个图再看源码,比较好理解。
执行过程大概是这样的。
就是什么意思呢?由DefaultSqlSessionFactory生成一个执行器Executor,下面是由BaseExecutor支撑的,里面包含一些配置和我们的一级缓存(是不是更深入的知道为啥一级缓存生命短了),同时也有一个类似装饰器的CachingExecutor,他的下面也是需要BaseExecutor来支撑的,需要查询时优先查缓存,查询不到回到我们的BaseExecutor来执行。搂一眼源码去,sqlSessionFactory.openSession()方法也就得到了我们的sqlsession,我们进去看一下都写了什么。
里面的95行tx主要是从我们的configuration中拿到一些数据源的配置,也就是我们图中画的来支撑BaseExecutor的配置,来到96行,创建Executor,传入了数据源配置和一个执行类型,这里简单提一嘴,类型主要有三种:SIMPLE(简单), REUSE(可重复使用), BATCH(批量进行),一般我们都用的SIMPLE。我们再进我们的Executor的创建方法看看,他们都做了什么事。
先判断了执行器类型,简单,可重复,批量,613行就是我们的缓存执行器了,外面那个判断就是你是否配置了二级缓存,从而是否配置我们的缓存执行器,底层还是Executor,可以点击进去看看的。
就是说我们由配置文件config.xml,mapper.xml生成了我们的Configuration对象,将Configuration放置在DefaultSqlSessionFactory内,生成了我们的Executor执行器,准备执行SQL。回到主题我们继续看下一部分,最后一个MappedStatement
MappedStatement解析:
记住两个问题,增删改查,可以归类为两种,一种是原来的数据变化-增删改,另一种是原来的数据没有变化-查询。我们的mybatis也是这样来处理的,主要就是query和update两种大类方法。
MappedStatement是由调用Executor执行器前,由configuration对象来构建的。源码在DefaultSqlSession的select***方法内,这里就不详细说了。感兴趣的可以自己去了解一下。我们主要来说执行流程。
执行过程大致是,1.拿到sql;2.拼接参数;3.执行sql;4.封装结果集。我们来看一下具体的源码流程。
我们上面得知,执行器的真正执行都是在BaseExecutor里来执行的,我们在DefaultSqlSession调用的select方法,最后执行了executor.query,只优先走我们的CachingExecutor执行器,如果没有才到我们的BaseExecutor执行器里面来,我们的sql是查询,不需要变动数据,那么我们把断点打在我们的BaseExecutor类的query方法上。
147行是清理我们的一级缓存,如果在mapper.xml配置了清理标签,这里会先清理一级缓存。queryStack表示这个执行器是否正在被使用。
152行开始查询我们的一级缓存,如果为空调用156行,开始我们正式的查询,里面是放入缓存占位置,然后执行查询,清理缓存的占位重新放置缓存,这里说的都是一级缓存了。
总结一下就是:
1.拿到流文件config.xml和mapper.xml;
2.用我们的两个或多个流文件创建一个Configuration对象,(单一职责原则来构建的,很多个构造器来构建的,例如XMLConfigBuilder)
3.将Configuration对象塞给SqlSessionFactoryBuilder类的build方法,构建SqlSessionFactory对象。
4.sqlSessionFactory.openSession()拿到我们的session对象。
5.由session对象和Configuration对象封装参数和结果集映射,产生对应的执行器来,二级缓存执行器和BaseExecutor执行器。
6.由二级缓存执行器CachingExecutor来优先查询二级缓存是否存在,不存在执行BaseExecutor执行器的query或update方法。
7.拿到结果集转换ResultMap,session关闭,写入二级缓存,返回结束。
最进弄了一个公众号,小菜技术,欢迎大家的加入